From 4a6bc933e196d0f281bf6851e49729125a733699 Mon Sep 17 00:00:00 2001 From: kenji Date: Mon, 27 Jul 2020 11:47:26 +0200 Subject: [PATCH 01/98] Manage swift5 string literals --- .../pmd/lang/swift/antlr4/Swift.g4 | 9 ++++- .../lang/swift/cpd/testdata/Swift5.0.swift | 8 +++++ .../pmd/lang/swift/cpd/testdata/Swift5.0.txt | 33 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 b/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 index 01ee4ad84c..c71b5fbda7 100644 --- a/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 +++ b/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 @@ -1018,7 +1018,7 @@ ImplicitParameterName : '$' DecimalLiteral ; // TODO: don't allow '_' here // GRAMMAR OF A LITERAL booleanLiteral: BooleanLiteral ; -literal : numericLiteral | MultiStringLiteral | SingleStringLiteral | BooleanLiteral | NilLiteral ; +literal : numericLiteral | MultiStringLiteral | DashedSingleStringLiteral | SingleStringLiteral | BooleanLiteral | NilLiteral ; // GRAMMAR OF AN INTEGER LITERAL @@ -1078,6 +1078,13 @@ fragment MultiQuotedTextItem : MultiInterpolatedString ; fragment MultiInterpolatedString: '\\(' (MultiQuotedTextItem | SingleStringLiteral)* ')'; +DashedSingleStringLiteral : '#"' DashedMultiQuotedText? '"#' ; +fragment DashedMultiQuotedText : DashedMultiQuotedTextItem+ ; +fragment DashedMultiQuotedTextItem : EscapedCharacter | DashedMultiInterpolatedString + | ~[\\\u000A\u000D] + ; +fragment DashedMultiInterpolatedString: '\\#(' (DashedMultiQuotedTextItem | DashedSingleStringLiteral)* ')'; + // StringLiteral : '"' QuotedText? '"' ; SingleStringLiteral : '"' QuotedText? '"' ; fragment SingleDoubleQuote : '"' | ~["] ; diff --git a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift index 6e77f1dd29..cf326c21c7 100644 --- a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift +++ b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift @@ -38,3 +38,11 @@ var x = 2 print(x[keyPath: id]) // prints 2 x[keyPath: id] = 3 print(x[keyPath: id]) // prints 3 + +// https://www.swiftbysundell.com/articles/string-literals-in-swift/ +let rawString = #"Press "Continue" to close this dialog."# +extension URL { + func html(withTitle title: String) -> String { + return #"\#(title)"# + } +} diff --git a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt index 5eaa13610c..0a89406a83 100644 --- a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt +++ b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt @@ -197,4 +197,37 @@ L40 [id] 18 19 [\]] 20 20 [)] 21 21 +L43 + [let] 1 3 + [rawString] 5 13 + [=] 15 15 + [#"Press "Continue" to close this d[ 17 58 +L44 + [extension] 1 9 + [URL] 11 13 + [{] 15 15 +L45 + [func] 5 8 + [html] 10 13 + [(] 14 14 + [withTitle] 15 23 + [title] 25 29 + [:] 30 30 + [String] 32 37 + [)] 38 38 + [->] 40 41 + [String] 43 48 + [{] 50 50 +L46 + [return] 9 14 + [#"\\[ 16 61 +L47 + [}] 5 5 +L48 + [}] 1 1 +L49 + [let] 1 3 + [regex] 5 9 + [=] 11 11 + [#"w+"#] 13 18 EOF From ddf55c7f8115ba79a597cabfd1e629ac3822df32 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Thu, 22 Oct 2020 11:30:45 -0700 Subject: [PATCH 02/98] Provide type information to Visualforce rules Addresses the general issue raised in https://github.com/pmd/pmd/issues/1092 This commit removes false positives from expressions in apex tags. The specific use case raised in 1092 isn't reproducible and represents a false negative that will be fixed separately. The existing Visualforce rules don't have any information about the data types referenced in the Visualforce page. This results in false positives when attempting to identify expressions that are vulnerable to XSS attacks. The rules should not warn about XSS attacks when the expression refers to a type such as Integer or Boolean. The VfExpressionTypeVisitor visits the Visualforce page and extracts the datatypes from Salesforce metadata. Data type information can come from either Apex classes or Object Fields. The Salesforce metadata is generally located in a sibling directory of the Visualforce directory. By default the code looks in directories relative to the Visualforce file to find the metadata. The conventional locations for the metadata are "../classes" and "../objects", the user can override this default with other directories if required. --- pmd-visualforce/pom.xml | 6 + .../pmd/lang/vf/ApexClassPropertyTypes.java | 131 ++++++ .../vf/ApexClassPropertyTypesVisitor.java | 91 ++++ .../pmd/lang/vf/ExpressionType.java | 128 +++++ .../pmd/lang/vf/ObjectFieldTypes.java | 279 +++++++++++ .../pmd/lang/vf/VfExpressionTypeVisitor.java | 183 +++++++ .../AbstractVfTypedElExpressionRule.java | 89 ++++ .../vf/rule/security/VfUnescapeElRule.java | 10 +- .../lang/vf/ApexClassPropertyTypesTest.java | 87 ++++ .../vf/ApexClassPropertyTypesVisitorTest.java | 66 +++ .../pmd/lang/vf/ExpressionTypeTest.java | 38 ++ .../pmd/lang/vf/ObjectFieldTypesTest.java | 117 +++++ .../pmd/lang/vf/RuleSetFactoryTest.java | 8 +- .../pmd/lang/vf/VFTestContstants.java | 25 + .../vf/rule/security/VfUnescapeElTest.java | 156 +++++- .../vf/metadata-mdapi/objects/Account.object | 445 ++++++++++++++++++ .../metadata-mdapi/pages/StandardAccount.page | 14 + .../metadata-sfdx/classes/ApexController.cls | 39 ++ .../metadata-sfdx/classes/ApexExtension1.cls | 4 + .../metadata-sfdx/classes/ApexExtension2.cls | 4 + .../ApexWithConflictingPropertyTypes.cls | 7 + .../Account/fields/Checkbox__c.field-meta.xml | 9 + .../Account/fields/DateTime__c.field-meta.xml | 9 + .../fields/LongTextArea__c.field-meta.xml | 10 + .../Account/fields/Picklist__c.field-meta.xml | 24 + .../Account/fields/TextArea__c.field-meta.xml | 9 + .../Account/fields/Text__c.field-meta.xml | 11 + .../metadata-sfdx/pages/ApexController.page | 11 + .../metadata-sfdx/pages/StandardAccount.page | 13 + .../pages/StandardAccountWithExtensions.page | 17 + 30 files changed, 2035 insertions(+), 5 deletions(-) create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor.java create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ExpressionType.java create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitorTest.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ExpressionTypeTest.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestContstants.java create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-mdapi/objects/Account.object create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-mdapi/pages/StandardAccount.page create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexController.cls create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexExtension1.cls create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexExtension2.cls create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexWithConflictingPropertyTypes.cls create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Checkbox__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/DateTime__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Picklist__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/TextArea__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Text__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/ApexController.page create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/StandardAccount.page create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/StandardAccountWithExtensions.page diff --git a/pmd-visualforce/pom.xml b/pmd-visualforce/pom.xml index d26901c00e..2d8178b61d 100644 --- a/pmd-visualforce/pom.xml +++ b/pmd-visualforce/pom.xml @@ -77,6 +77,12 @@ pmd-core + + net.sourceforge.pmd + pmd-apex + ${project.version} + + junit junit diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java new file mode 100644 index 0000000000..2c2528d4b1 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java @@ -0,0 +1,131 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +import org.apache.commons.lang3.tuple.Pair; + +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.Parser; +import net.sourceforge.pmd.lang.ParserOptions; +import net.sourceforge.pmd.lang.apex.ApexLanguageModule; +import net.sourceforge.pmd.lang.apex.ast.ApexNode; +import net.sourceforge.pmd.lang.ast.Node; + +import apex.jorje.semantic.symbol.type.BasicType; +import com.google.common.collect.Sets; + +/** + * Responsible for storing a mapping of Apex Class properties that can be referenced from Visualforce to the type of the + * property. + */ +class ApexClassPropertyTypes { + private static final Logger LOGGER = Logger.getLogger(ApexClassPropertyTypes.class.getName()); + private static final String APEX_CLASS_FILE_SUFFIX = ".cls"; + + private final ConcurrentHashMap variableNameToVariableType; + private final Set variableNameProcessed; + + ApexClassPropertyTypes() { + this.variableNameToVariableType = new ConcurrentHashMap<>(); + this.variableNameProcessed = Sets.newConcurrentHashSet(); + } + + /** + * Looks in {@code apexDirectories} for an Apex property identified by {@code expression}. + * + * @return the ExpressionType for the property represented by {@code expression} or null if not found. + */ + public ExpressionType getVariableType(String expression, String vfFileName, List apexDirectories) { + String lowerExpression = expression.toLowerCase(Locale.ROOT); + if (variableNameToVariableType.containsKey(lowerExpression)) { + // The expression has been previously retrieved + return variableNameToVariableType.get(lowerExpression); + } else if (variableNameProcessed.contains(lowerExpression)) { + // The expression has been previously requested, but was not found + return null; + } else { + String[] parts = expression.split("\\."); + if (parts.length >= 2) { + // Load the class and parse it + String className = parts[0]; + + Path vfFilePath = Paths.get(vfFileName); + for (String apexDirectory : apexDirectories) { + Path candidateDirectory; + if (Paths.get(apexDirectory).isAbsolute()) { + candidateDirectory = Paths.get(apexDirectory); + } else { + candidateDirectory = vfFilePath.getParent().resolve(apexDirectory); + } + + Path apexFilePath = candidateDirectory.resolve(className + APEX_CLASS_FILE_SUFFIX); + if (Files.exists(apexFilePath) && Files.isRegularFile(apexFilePath)) { + Parser parser = getApexParser(); + try (BufferedReader reader = Files.newBufferedReader(apexFilePath, StandardCharsets.UTF_8)) { + Node node = parser.parse(apexFilePath.toString(), reader); + ApexClassPropertyTypesVisitor visitor = new ApexClassPropertyTypesVisitor(); + visitor.visit((ApexNode) node, null); + for (Pair variable : visitor.getVariables()) { + setVariableType(variable.getKey(), variable.getValue()); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + if (variableNameToVariableType.containsKey(lowerExpression)) { + // Break out of the loop if a variable was found + break; + } + } + } + variableNameProcessed.add(lowerExpression); + } else { + throw new RuntimeException("Malformed expression: " + expression); + } + } + + return variableNameToVariableType.get(lowerExpression); + } + + private void setVariableType(String name, BasicType basicType) { + ExpressionType expressionType = ExpressionType.fromBasicType(basicType); + ExpressionType previousType = variableNameToVariableType.put(name.toLowerCase(Locale.ROOT), expressionType); + if (previousType != null && !previousType.equals(expressionType)) { + // It is possible to have a property and method with different types that appear the same to this code. An + // example is an Apex class with a property "public String Foo {get; set;}" and a method of + // "Integer getFoo() { return 1; }". In this case set the value as Unknown because we can't be sure which it + // is. This code could be more complex in an attempt to determine if all the types are safe from escaping, + // but we will allow a false positive in order to let the user know that the code could be refactored to be + // more clear. + variableNameToVariableType.put(name.toLowerCase(Locale.ROOT), ExpressionType.Unknown); + LOGGER.warning("Conflicting types for " + + name + + ". CurrentType=" + + expressionType + + ", PreviousType=" + + previousType); + } + } + + private Parser getApexParser() { + LanguageVersion languageVersion = LanguageRegistry.getLanguage(ApexLanguageModule.NAME).getDefaultVersion(); + ParserOptions parserOptions = languageVersion.getLanguageVersionHandler().getDefaultParserOptions(); + return languageVersion.getLanguageVersionHandler().getParser(parserOptions); + } +} + diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor.java new file mode 100644 index 0000000000..18fd9408c2 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor.java @@ -0,0 +1,91 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang3.tuple.Pair; + +import net.sourceforge.pmd.lang.apex.ast.ASTMethod; +import net.sourceforge.pmd.lang.apex.ast.ASTModifierNode; +import net.sourceforge.pmd.lang.apex.ast.ASTUserClass; +import net.sourceforge.pmd.lang.apex.ast.ApexNode; +import net.sourceforge.pmd.lang.apex.ast.ApexParserVisitorAdapter; + +import apex.jorje.semantic.symbol.member.method.Generated; +import apex.jorje.semantic.symbol.member.method.MethodInfo; +import apex.jorje.semantic.symbol.type.BasicType; + +/** + * Visits an Apex class to determine a mapping of referenceable expressions to expression type. + */ +final class ApexClassPropertyTypesVisitor extends ApexParserVisitorAdapter { + + /** + * Prefix for standard bean type getters, i.e. getFoo + */ + private static final String BEAN_GETTER_PREFIX = "get"; + /** + * This is the prefix assigned to automatic get/set properties such as String myProp { get; set; } + */ + private static final String PROPERTY_PREFIX_ACCESSOR = "__sfdc_"; + + private static final String RETURN_TYPE_VOID = "void"; + + /** + * Pairs of (variableName, expressionType) + */ + private final List> variables; + + ApexClassPropertyTypesVisitor() { + this.variables = new ArrayList<>(); + } + + public List> getVariables() { + return this.variables; + } + + /** + * Stores the return type of the method in {@link #variables} if the method is referenceable from a + * Visualforce page. + */ + @Override + public Object visit(ASTMethod node, Object data) { + MethodInfo mi = node.getNode().getMethodInfo(); + if (mi.getParameterTypes().isEmpty() + && isVisibleToVisualForce(node) + && !RETURN_TYPE_VOID.equalsIgnoreCase(mi.getReturnType().getApexName()) + && (mi.getGenerated().equals(Generated.USER) || mi.isPropertyAccessor())) { + StringBuilder sb = new StringBuilder(); + List parents = node.getParentsOfType(ASTUserClass.class); + Collections.reverse(parents); + for (ASTUserClass parent : parents) { + sb.append(parent.getImage()).append("."); + } + String name = node.getImage(); + for (String prefix : new String[]{BEAN_GETTER_PREFIX, PROPERTY_PREFIX_ACCESSOR}) { + if (name.startsWith(prefix)) { + name = name.substring(prefix.length()); + } + } + sb.append(name); + + variables.add(Pair.of(sb.toString(), mi.getReturnType().getBasicType())); + } + return super.visit((ApexNode) node, data); + } + + /** + * Used to filter out methods that aren't visible to the Visualforce page. + * + * @return true if the method is visible to Visualforce. + */ + private boolean isVisibleToVisualForce(ASTMethod node) { + ASTModifierNode modifier = node.getFirstChildOfType(ASTModifierNode.class); + return modifier.isGlobal() | modifier.isPublic(); + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ExpressionType.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ExpressionType.java new file mode 100644 index 0000000000..c6d327b3ab --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ExpressionType.java @@ -0,0 +1,128 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import java.util.Arrays; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +import apex.jorje.semantic.symbol.type.BasicType; +import com.google.common.base.Strings; +import com.google.common.collect.Sets; + +/** + * Represents all data types that can be referenced from a Visualforce page. This enum consolidates the data types + * available to CustomFields and Apex. It uses the naming convention of CustomFields. + * + * See https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_field_types.htm#meta_type_fieldtype + */ +public enum ExpressionType { + AutoNumber(false), + Checkbox(false, BasicType.BOOLEAN), + Currency(false, BasicType.CURRENCY), + Date(false, BasicType.DATE), + DateTime(false, BasicType.DATE_TIME), + Email(false), + EncryptedText(true), + ExternalLookup(true), + File(false), + Hierarchy(false), + Html(true), + IndirectLookup(false), + Location(false), + LongTextArea(true), + Lookup(false, BasicType.ID), + MasterDetail(false), + MetadataRelationship(false), + MultiselectPicklist(true), + Note(true), + Number(false, BasicType.DECIMAL, BasicType.DOUBLE, BasicType.INTEGER, BasicType.LONG), + Percent(false), + Phone(false), + Picklist(true), + Summary(false), + Text(true, BasicType.STRING), + TextArea(true), + Time(false, BasicType.TIME), + Url(false), + Unknown(true); + + private static final Logger LOGGER = Logger.getLogger(ExpressionType.class.getName()); + + + /** + * True if this field is an XSS risk + */ + public final boolean requiresEscaping; + + /** + * The set of {@link BasicType}s that map to this type. Multiple types can map to a single instance of this enum. + */ + private final Set basicTypes; + + /** + * A case insensitive map of the enum name to its instance. The case metadata is not guaranteed to have the correct + * case. + */ + private static final Map CASE_INSENSITIVE_MAP = new ConcurrentHashMap<>(); + + /** + * Map of BasicType to ExpressionType. Multiple BasicTypes may map to one ExrpessionType. + */ + private static final Map BASIC_TYPE_MAP = new ConcurrentHashMap<>(); + + static { + for (ExpressionType expressionType : ExpressionType.values()) { + CASE_INSENSITIVE_MAP.put(expressionType.name().toLowerCase(Locale.ROOT), expressionType); + for (BasicType basicType : expressionType.basicTypes) { + BASIC_TYPE_MAP.put(basicType, expressionType); + } + } + } + + /** + * Map to correct instance, returns {@code Unknown} if the value can't be mapped. + */ + public static ExpressionType fromString(String value) { + value = Strings.nullToEmpty(value); + ExpressionType expressionType = CASE_INSENSITIVE_MAP.get(value.toLowerCase(Locale.ROOT)); + + if (expressionType == null) { + expressionType = ExpressionType.Unknown; + LOGGER.fine("Unable to determine ExpressionType of " + value); + } + + return expressionType; + } + + /** + * Map to correct instance, returns {@code Unknown} if the value can't be mapped. + */ + public static ExpressionType fromBasicType(BasicType value) { + ExpressionType expressionType = value != null ? BASIC_TYPE_MAP.get(value) : null; + + if (expressionType == null) { + expressionType = ExpressionType.Unknown; + LOGGER.fine("Unable to determine ExpressionType of " + value); + } + + return expressionType; + } + + ExpressionType(boolean requiresEscaping) { + this(requiresEscaping, null); + } + + ExpressionType(boolean requiresEscaping, BasicType...basicTypes) { + this.requiresEscaping = requiresEscaping; + this.basicTypes = Sets.newConcurrentHashSet(); + if (basicTypes != null) { + this.basicTypes.addAll(Arrays.asList(basicTypes)); + } + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java new file mode 100644 index 0000000000..9ad7bdbe3c --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java @@ -0,0 +1,279 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Sets; + +/** + * Responsible for storing a mapping of Fields that can be referenced from Visualforce to the type of the field. + */ +class ObjectFieldTypes { + private static final Logger LOGGER = Logger.getLogger(ObjectFieldTypes.class.getName()); + + public static final String CUSTOM_OBJECT_SUFFIX = "__c"; + private static final String FIELDS_DIRECTORY = "fields"; + private static final String MDAPI_OBJECT_FILE_SUFFIX = ".object"; + private static final String SFDX_FIELD_FILE_SUFFIX = ".field-meta.xml"; + + private static final ImmutableMap STANDARD_FIELD_TYPES = + ImmutableMap.builder() + .put("createdbyid", ExpressionType.Lookup) + .put("createddate", ExpressionType.DateTime) + .put("id", ExpressionType.Lookup) + .put("isdeleted", ExpressionType.Checkbox) + .put("lastmodifiedbyid", ExpressionType.Lookup) + .put("lastmodifieddate", ExpressionType.DateTime) + .put("systemmodstamp", ExpressionType.DateTime) + .build(); + + /** + * Cache of lowercase variable names to the variable type declared in the field's metadata file. + */ + private final ConcurrentHashMap variableNameToVariableType; + + /** + * Keep track of which variables were already processed. Avoid processing if a page repeatedly asks for an entry + * which we haven't previously found. + */ + private final Set variableNameProcessed; + + /** + * Keep track of which ".object" files have already been processed. All fields are processed at once. If an object + * file has been processed + */ + private final Set objectFileProcessed; + + // XML Parsing objects + private final DocumentBuilder documentBuilder; + private final XPathExpression customObjectFieldsExpression; + private final XPathExpression customFieldFullNameExpression; + private final XPathExpression customFieldTypeExpression; + private final XPathExpression sfdxCustomFieldFullNameExpression; + private final XPathExpression sfdxCustomFieldTypeExpression; + + ObjectFieldTypes() { + this.variableNameToVariableType = new ConcurrentHashMap<>(); + this.variableNameProcessed = Sets.newConcurrentHashSet(); + this.objectFileProcessed = Sets.newConcurrentHashSet(); + + try { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(false); + documentBuilderFactory.setValidating(false); + documentBuilderFactory.setIgnoringComments(true); + documentBuilderFactory.setIgnoringElementContentWhitespace(true); + documentBuilderFactory.setExpandEntityReferences(false); + documentBuilderFactory.setCoalescing(false); + documentBuilderFactory.setXIncludeAware(false); + documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); + documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + documentBuilder = documentBuilderFactory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + throw new RuntimeException(e); + } + + try { + XPath xPath = XPathFactory.newInstance().newXPath(); + this.customObjectFieldsExpression = xPath.compile("/CustomObject/fields"); + this.customFieldFullNameExpression = xPath.compile("fullName/text()"); + this.customFieldTypeExpression = xPath.compile("type/text()"); + this.sfdxCustomFieldFullNameExpression = xPath.compile("/CustomField/fullName/text()"); + this.sfdxCustomFieldTypeExpression = xPath.compile("/CustomField/type/text()"); + } catch (XPathExpressionException e) { + throw new RuntimeException(e); + } + } + + /** + * Looks in {@code objectsDirectories} for a custom field identified by {@code expression}. + * + * @return the ExpressionType for the field represented by {@code expression} or null the custom field isn't found. + */ + public ExpressionType getVariableType(String expression, String vfFileName, List objectsDirectories) { + String lowerExpression = expression.toLowerCase(Locale.ROOT); + + if (variableNameToVariableType.containsKey(lowerExpression)) { + // The expression has been previously retrieved + return variableNameToVariableType.get(lowerExpression); + } else if (variableNameProcessed.contains(lowerExpression)) { + // The expression has been previously requested, but was not found + return null; + } else { + // The expression should be in the form . + String[] parts = expression.split("\\."); + if (parts.length == 1) { + throw new RuntimeException("Malformed identifier: " + expression); + } else if (parts.length == 2) { + String objectName = parts[0]; + String fieldName = parts[1]; + + addStandardFields(objectName); + + // Attempt to find a metadata file that contains the custom field. The information will be located in a + // file located at /.object or in an file located at + // //fields/.field-meta.xml. The list of object directories + // defaults to the [/../objects] but can be overridden by the user. + Path vfFilePath = Paths.get(vfFileName); + for (String objectsDirectory : objectsDirectories) { + Path candidateDirectory; + if (Paths.get(objectsDirectory).isAbsolute()) { + candidateDirectory = Paths.get(objectsDirectory); + } else { + candidateDirectory = vfFilePath.getParent().resolve(objectsDirectory); + } + + Path sfdxCustomFieldPath = getSfdxCustomFieldPath(candidateDirectory, objectName, fieldName); + if (sfdxCustomFieldPath != null) { + // SFDX Format + parseSfdxCustomField(objectName, sfdxCustomFieldPath); + } else { + // MDAPI Format + String fileName = objectName + MDAPI_OBJECT_FILE_SUFFIX; + Path mdapiPath = candidateDirectory.resolve(fileName); + if (Files.exists(mdapiPath) && Files.isRegularFile(mdapiPath)) { + parseMdapiCustomObject(mdapiPath); + } + } + + if (variableNameToVariableType.containsKey(lowerExpression)) { + // Break out of the loop if a variable was found + break; + } + } + variableNameProcessed.add(lowerExpression); + } else { + // TODO: Support cross object relationships, these are expressions that contain "__r" + LOGGER.fine("Expression does not have two parts: " + expression); + } + } + + return variableNameToVariableType.get(lowerExpression); + } + + /** + * Sfdx projects decompose custom fields into individual files. This method will return the individual file that + * corresponds to <objectName>.<fieldName> if it exists. + * + * @return path to the metadata file for the Custom Field or null if not found + */ + private Path getSfdxCustomFieldPath(Path objectsDirectory, String objectName, String fieldName) { + Path fieldsDirectoryPath = Paths.get(objectsDirectory.toString(), objectName, FIELDS_DIRECTORY); + if (Files.exists(fieldsDirectoryPath) && Files.isDirectory(fieldsDirectoryPath)) { + Path sfdxFieldPath = Paths.get(fieldsDirectoryPath.toString(), fieldName + SFDX_FIELD_FILE_SUFFIX); + if (Files.exists(sfdxFieldPath) && Files.isRegularFile(sfdxFieldPath)) { + return sfdxFieldPath; + } + } + return null; + } + + /** + * Determine the type of the custom field. + */ + private void parseSfdxCustomField(String customObjectName, Path sfdxCustomFieldPath) { + try { + Document document = documentBuilder.parse(sfdxCustomFieldPath.toFile()); + Node fullNameNode = (Node) sfdxCustomFieldFullNameExpression.evaluate(document, XPathConstants.NODE); + Node typeNode = (Node) sfdxCustomFieldTypeExpression.evaluate(document, XPathConstants.NODE); + String type = typeNode.getNodeValue(); + ExpressionType expressionType = ExpressionType.fromString(type); + + String key = customObjectName + "." + fullNameNode.getNodeValue(); + setVariableType(key, expressionType); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Parse the custom object path and determine the type of all of its custom fields. + */ + private void parseMdapiCustomObject(Path mdapiObjectFile) { + String fileName = mdapiObjectFile.getFileName().toString(); + + String customObjectName = fileName.substring(0, fileName.lastIndexOf(MDAPI_OBJECT_FILE_SUFFIX)); + if (!objectFileProcessed.contains(customObjectName)) { + try { + Document document = documentBuilder.parse(mdapiObjectFile.toFile()); + NodeList fieldsNodes = (NodeList) customObjectFieldsExpression.evaluate(document, XPathConstants.NODESET); + for (int i = 0; i < fieldsNodes.getLength(); i++) { + Node fieldsNode = fieldsNodes.item(i); + Node fullNameNode = (Node) customFieldFullNameExpression.evaluate(fieldsNode, XPathConstants.NODE); + if (fullNameNode == null) { + throw new RuntimeException("fullName evaluate failed for " + customObjectName + " " + fieldsNode.getTextContent()); + } + String name = fullNameNode.getNodeValue(); + if (endsWithIgnoreCase(name, CUSTOM_OBJECT_SUFFIX)) { + Node typeNode = (Node) customFieldTypeExpression.evaluate(fieldsNode, XPathConstants.NODE); + if (typeNode == null) { + throw new RuntimeException("type evaluate failed for object=" + customObjectName + ", field=" + name + " " + fieldsNode.getTextContent()); + } + String type = typeNode.getNodeValue(); + ExpressionType expressionType = ExpressionType.fromString(type); + String key = customObjectName + "." + fullNameNode.getNodeValue(); + setVariableType(key, expressionType); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + objectFileProcessed.add(customObjectName); + } + } + + /** + * Add the set of standard fields which aren't present in the metadata file, but may be refernced from the + * visualforce page. + */ + private void addStandardFields(String customObjectName) { + for (Map.Entry entry : STANDARD_FIELD_TYPES.entrySet()) { + setVariableType(customObjectName + "." + entry.getKey(), entry.getValue()); + } + } + + /** + * Null safe endsWithIgnoreCase + */ + private boolean endsWithIgnoreCase(String str, String suffix) { + return str != null && str.toLowerCase(Locale.ROOT).endsWith(suffix.toLowerCase(Locale.ROOT)); + } + + private void setVariableType(String name, ExpressionType expressionType) { + name = name.toLowerCase(Locale.ROOT); + ExpressionType previousType = variableNameToVariableType.put(name, expressionType); + if (previousType != null && !previousType.equals(expressionType)) { + // It should not be possible ot have conflicting types for CustomFields + throw new RuntimeException("Conflicting types for " + + name + + ". CurrentType=" + + expressionType + + ", PreviousType=" + + previousType); + } + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java new file mode 100644 index 0000000000..8e03a20a8d --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java @@ -0,0 +1,183 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import java.util.ArrayList; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.logging.Logger; + +import net.sourceforge.pmd.lang.vf.ast.ASTAttribute; +import net.sourceforge.pmd.lang.vf.ast.ASTAttributeValue; +import net.sourceforge.pmd.lang.vf.ast.ASTDotExpression; +import net.sourceforge.pmd.lang.vf.ast.ASTElExpression; +import net.sourceforge.pmd.lang.vf.ast.ASTElement; +import net.sourceforge.pmd.lang.vf.ast.ASTExpression; +import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; +import net.sourceforge.pmd.lang.vf.ast.ASTText; +import net.sourceforge.pmd.lang.vf.ast.VfParserVisitorAdapter; + +/** + * Visits {@link ASTElExpression} nodes and stores type information for all {@link ASTIdentifier} child nodes. + */ +public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { + private static final Logger LOGGER = Logger.getLogger(VfExpressionTypeVisitor.class.getName()); + + private static final String APEX_PAGE = "apex:page"; + private static final String CONTROLLER_ATTRIBUTE = "controller"; + private static final String STANDARD_CONTROLLER_ATTRIBUTE = "standardcontroller"; + private static final String EXTENSIONS_ATTRIBUTE = "extensions"; + + private ApexClassPropertyTypes apexClassPropertyTypes; + private ObjectFieldTypes objectFieldTypes; + private final String fileName; + private String standardControllerName; + private final IdentityHashMap expressionTypes; + + /** + * List of all Apex Class names that the VF page might refer to. These values come from either the + * {@code controller} or {@code extensions} attribute. + */ + private final List apexClassNames; + private final List apexDirectories; + private final List objectsDirectories; + + public VfExpressionTypeVisitor(String fileName, List apexDirectories, List objectsDirectories) { + this.fileName = fileName; + this.apexDirectories = apexDirectories; + this.objectsDirectories = objectsDirectories; + this.apexClassPropertyTypes = new ApexClassPropertyTypes(); + this.objectFieldTypes = new ObjectFieldTypes(); + this.apexClassNames = new ArrayList<>(); + this.expressionTypes = new IdentityHashMap<>(); + } + + public IdentityHashMap getExpressionTypes() { + return this.expressionTypes; + } + + /** + * Gather names of Controller, Extensions, and StandardController. Each of these may contain the identifier + * referenced from the Visualforce page. + */ + @Override + public Object visit(ASTElement node, Object data) { + if (APEX_PAGE.equalsIgnoreCase(node.getName())) { + List attribs = node.findChildrenOfType(ASTAttribute.class); + + for (ASTAttribute attr : attribs) { + String lowerAttr = attr.getName().toLowerCase(Locale.ROOT); + if (CONTROLLER_ATTRIBUTE.equals(lowerAttr)) { + // Controller Name should always take precedence + apexClassNames.add(0, attr.getFirstChildOfType(ASTAttributeValue.class) + .getFirstChildOfType(ASTText.class).getImage()); + break; + } else if (STANDARD_CONTROLLER_ATTRIBUTE.equals(lowerAttr)) { + standardControllerName = attr.getFirstChildOfType(ASTAttributeValue.class) + .getFirstChildOfType(ASTText.class).getImage().toLowerCase(Locale.ROOT); + } else if (EXTENSIONS_ATTRIBUTE.equalsIgnoreCase(lowerAttr)) { + for (String extension : attr.getFirstChildOfType(ASTAttributeValue.class) + .getFirstChildOfType(ASTText.class).getImage().split(",")) { + apexClassNames.add(extension.trim()); + } + } + } + } + return super.visit(node, data); + } + + /** + * Find all {@link ASTIdentifier} child nodes of {@code node} and attempt to resolve their type. The order of + * precedence is Controller, Extensions, StandardController. + */ + @Override + public Object visit(ASTElExpression node, Object data) { + for (Map.Entry entry : getExpressionIdentifierNames(node).entrySet()) { + String name = entry.getValue(); + ExpressionType type = null; + String[] parts = name.split("\\."); + + // Apex extensions take precedence over Standard controllers. + // The example below will display "Name From Inner Class" instead of the Account name + // public class AccountExtension { + // public AccountExtension(ApexPages.StandardController controller) { + // } + // + // public InnerClass getAccount() { + // return new InnerClass(); + // } + // + // public class InnerClass { + // public String getName() { + // return 'Name From Inner Class'; + // } + // } + // } + // + // + // + + // Try to find the identifier in an Apex class + for (String apexClassName : apexClassNames) { + String fullName = apexClassName + "." + name; + type = apexClassPropertyTypes.getVariableType(fullName, fileName, apexDirectories); + if (type != null) { + break; + } + } + + // Try to find the identifier in a CustomField if it wasn't found in an Apex class and the identifier corresponds + // to the StandardController. + if (type == null) { + if (parts.length >= 2 && standardControllerName != null && standardControllerName.equalsIgnoreCase(parts[0])) { + type = objectFieldTypes.getVariableType(name, fileName, objectsDirectories); + } + } + + if (type != null) { + expressionTypes.put(entry.getKey(), type); + } else { + LOGGER.fine("Unable to determine type for: " + name); + } + } + return super.visit(node, data); + } + + /** + * Parse the expression returning all of the identifiers in that expression mapped to its string represenation. + * An {@code ASTElExpression} can contain multiple {@code ASTExpressions} in cases of logical operators. + */ + private Map getExpressionIdentifierNames(ASTElExpression elExpression) { + Map identifierToName = new IdentityHashMap<>(); + + for (ASTExpression expression : elExpression.findChildrenOfType(ASTExpression.class)) { + for (ASTIdentifier identifier : expression.findChildrenOfType(ASTIdentifier.class)) { + StringBuilder sb = new StringBuilder(identifier.getImage()); + + for (ASTDotExpression dotExpression : expression.findChildrenOfType(ASTDotExpression.class)) { + sb.append("."); + + List childIdentifiers = dotExpression.findChildrenOfType(ASTIdentifier.class); + if (childIdentifiers.isEmpty()) { + continue; + } else if (childIdentifiers.size() > 1) { + // The grammar guarantees tha there should be at most 1 child identifier + // (Identifier() | Literal() ) + throw new RuntimeException("Unexpected number of childIdentifiers: size=" + childIdentifiers.size()); + } + + ASTIdentifier childIdentifier = childIdentifiers.get(0); + sb.append(childIdentifier.getImage()); + } + + identifierToName.put(identifier, sb.toString()); + } + } + + return identifierToName; + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java new file mode 100644 index 0000000000..7a68a387d8 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java @@ -0,0 +1,89 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.rule.security; + +import java.io.File; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Map; + +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.lang.vf.ExpressionType; +import net.sourceforge.pmd.lang.vf.VfExpressionTypeVisitor; +import net.sourceforge.pmd.lang.vf.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; +import net.sourceforge.pmd.lang.vf.rule.AbstractVfRule; +import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertyFactory; + +/** + * Represents a rule where the {@link net.sourceforge.pmd.lang.vf.ast.ASTIdentifier} nodes are enhanced with the + * node's {@link ExpressionType}. This is achieved by processing metadata files referenced by the Visualforce page. + */ +class AbstractVfTypedElExpressionRule extends AbstractVfRule { + /** + * Directory that contains Apex classes that may be referenced from a Visualforce page. + */ + private static final PropertyDescriptor> APEX_DIRECTORIES_DESCRIPTOR = + PropertyFactory.stringListProperty("apexDirectories") + .desc("Location of Apex Class directories. Absolute or relative to the Visualforce directory") + .defaultValue(Collections.singletonList(".." + File.separator + "classes")) + .delim(',') + .build(); + + /** + * Directory that contains Object definitions that may be referenced from a Visualforce page. + */ + private static final PropertyDescriptor> OBJECTS_DIRECTORIES_DESCRIPTOR = + PropertyFactory.stringListProperty("objectsDirectories") + .desc("Location of CustomObject directories. Absolute or relative to the Visualforce directory") + .defaultValue(Collections.singletonList(".." + File.separator + "objects")) + .delim(',') + .build(); + + private Map expressionTypes; + + AbstractVfTypedElExpressionRule() { + definePropertyDescriptor(APEX_DIRECTORIES_DESCRIPTOR); + definePropertyDescriptor(OBJECTS_DIRECTORIES_DESCRIPTOR); + } + + public ExpressionType getExpressionType(ASTIdentifier node) { + return expressionTypes.get(node); + } + + @Override + public void start(RuleContext ctx) { + this.expressionTypes = Collections.synchronizedMap(new IdentityHashMap()); + super.start(ctx); + } + + /** + * Invoke {@link VfExpressionTypeVisitor#visit(ASTCompilationUnit, Object)} to identify Visualforce expression's and + * their types. + */ + @Override + public Object visit(ASTCompilationUnit node, Object data) { + List apexDirectories = getProperty(APEX_DIRECTORIES_DESCRIPTOR); + List objectsDirectories = getProperty(OBJECTS_DIRECTORIES_DESCRIPTOR); + + // The visitor will only find information if there are directories to look in. This allows users to disable the + // visitor in the unlikely scenario that they want to. + if (!apexDirectories.isEmpty() || !objectsDirectories.isEmpty()) { + RuleContext ctx = (RuleContext) data; + File file = ctx.getSourceCodeFile(); + if (file != null) { + VfExpressionTypeVisitor visitor = new VfExpressionTypeVisitor(file.getAbsolutePath(), + apexDirectories, + objectsDirectories); + visitor.visit(node, data); + this.expressionTypes.putAll(visitor.getExpressionTypes()); + } + } + + return super.visit(node, data); + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java index 51817b896c..6891033ac5 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java @@ -12,6 +12,7 @@ import java.util.Set; import java.util.regex.Pattern; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.vf.ExpressionType; import net.sourceforge.pmd.lang.vf.ast.ASTArguments; import net.sourceforge.pmd.lang.vf.ast.ASTAttribute; import net.sourceforge.pmd.lang.vf.ast.ASTContent; @@ -25,13 +26,12 @@ import net.sourceforge.pmd.lang.vf.ast.ASTLiteral; import net.sourceforge.pmd.lang.vf.ast.ASTNegationExpression; import net.sourceforge.pmd.lang.vf.ast.ASTText; import net.sourceforge.pmd.lang.vf.ast.AbstractVFNode; -import net.sourceforge.pmd.lang.vf.rule.AbstractVfRule; /** * @author sergey.gorbaty February 2017 * */ -public class VfUnescapeElRule extends AbstractVfRule { +public class VfUnescapeElRule extends AbstractVfTypedElExpressionRule { private static final String A_CONST = "a"; private static final String APEXIFRAME_CONST = "apex:iframe"; private static final String IFRAME_CONST = "iframe"; @@ -53,7 +53,6 @@ public class VfUnescapeElRule extends AbstractVfRule { @Override public Object visit(ASTHtmlScript node, Object data) { checkIfCorrectlyEscaped(node, data); - return super.visit(node, data); } @@ -412,6 +411,11 @@ public class VfUnescapeElRule extends AbstractVfRule { final List ids = expr.findChildrenOfType(ASTIdentifier.class); for (final ASTIdentifier id : ids) { + ExpressionType expressionType = getExpressionType(id); + if (expressionType != null && !expressionType.requiresEscaping) { + return false; + } + boolean isEscaped = false; for (Escaping e : escapes) { diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java new file mode 100644 index 0000000000..743b32c6e4 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java @@ -0,0 +1,87 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; + +import org.junit.Test; + +public class ApexClassPropertyTypesTest { + @Test + public void testApexClassIsProperlyParsed() { + Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "ApexController.page")) + .toAbsolutePath(); + String vfFileName = vfPagePath.toString(); + + // Intentionally use the wrong case for property names to ensure that they can be found. The Apex class name + // must have the correct case since it is used to lookup the file. The Apex class name is guaranteed to be correct + // in the Visualforce page, but the property names are not + ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); + assertEquals(ExpressionType.Lookup, + apexClassPropertyTypes.getVariableType("ApexController.accOuntIdProp", vfFileName, + VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + assertEquals(ExpressionType.Lookup, + apexClassPropertyTypes.getVariableType("ApexController.AcCountId", vfFileName, + VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + assertEquals(ExpressionType.Text, + apexClassPropertyTypes.getVariableType("ApexController.AcCountname", vfFileName, + VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + + // InnerController + assertEquals("The class should be parsed to Unknown. It's not a valid expression on its own.", + ExpressionType.Unknown, + apexClassPropertyTypes.getVariableType("ApexController.innErController", vfFileName, + VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + assertEquals(ExpressionType.Lookup, + apexClassPropertyTypes.getVariableType("ApexController.innErController.innErAccountIdProp", + vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + assertEquals(ExpressionType.Lookup, + apexClassPropertyTypes.getVariableType("ApexController.innErController.innErAccountid", + vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + assertEquals(ExpressionType.Text, + apexClassPropertyTypes.getVariableType("ApexController.innErController.innErAccountnAme", + vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + + assertNull("Invalid class should return null", + apexClassPropertyTypes.getVariableType("unknownclass.invalidProperty", vfFileName, + VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + assertNull("Invalid class property should return null", + apexClassPropertyTypes.getVariableType("ApexController.invalidProperty", vfFileName, + VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + } + + /** + * It is possible to have a property and method with different types that resolve to the same Visualforce + * expression. An example is an Apex class with a property "public String Foo {get; set;}" and a method of + * "Integer getFoo() { return 1; }". These properties should map to {@link ExpressionType#Unknown}. + */ + @Test + public void testConflictingPropertyTypesMapsToUnknown() { + Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "ApexController.page")) + .toAbsolutePath(); + String vfFileName = vfPagePath.toString(); + ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); + assertEquals(ExpressionType.Unknown, + apexClassPropertyTypes.getVariableType("ApexWithConflictingPropertyTypes.ConflictingProp", + vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + } + + @Test + public void testInvalidDirectoryDoesNotCauseAnException() { + Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "ApexController.page")) + .toAbsolutePath(); + String vfFileName = vfPagePath.toString(); + + List paths = Arrays.asList(Paths.get("..", "classes-does-not-exist").toString()); + ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); + assertNull(apexClassPropertyTypes.getVariableType("ApexController.accOuntIdProp", vfFileName, paths)); + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitorTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitorTest.java new file mode 100644 index 0000000000..5d7ec00550 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitorTest.java @@ -0,0 +1,66 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.io.BufferedReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.tuple.Pair; +import org.junit.Test; + +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.Parser; +import net.sourceforge.pmd.lang.ParserOptions; +import net.sourceforge.pmd.lang.apex.ApexLanguageModule; +import net.sourceforge.pmd.lang.apex.ast.ApexNode; +import net.sourceforge.pmd.lang.ast.Node; + +import apex.jorje.semantic.symbol.type.BasicType; + +public class ApexClassPropertyTypesVisitorTest { + @Test + public void testApexClassIsProperlyParsed() throws IOException { + LanguageVersion languageVersion = LanguageRegistry.getLanguage(ApexLanguageModule.NAME).getDefaultVersion(); + ParserOptions parserOptions = languageVersion.getLanguageVersionHandler().getDefaultParserOptions(); + Parser parser = languageVersion.getLanguageVersionHandler().getParser(parserOptions); + + Path apexPath = VFTestContstants.SFDX_PATH.resolve(Paths.get("classes", "ApexController.cls")).toAbsolutePath(); + ApexClassPropertyTypesVisitor visitor = new ApexClassPropertyTypesVisitor(); + try (BufferedReader reader = Files.newBufferedReader(apexPath, StandardCharsets.UTF_8)) { + Node node = parser.parse(apexPath.toString(), reader); + assertNotNull(node); + visitor.visit((ApexNode) node, null); + } + + List> variables = visitor.getVariables(); + assertEquals(7, variables.size()); + Map variableNameToVariableType = new Hashtable<>(); + for (Pair variable : variables) { + // Map the values and ensure there were no duplicates + BasicType previous = variableNameToVariableType.put(variable.getKey(), variable.getValue()); + assertNull(variable.getKey(), previous); + } + + assertEquals(BasicType.ID, variableNameToVariableType.get("ApexController.AccountIdProp")); + assertEquals(BasicType.ID, variableNameToVariableType.get("ApexController.AccountId")); + assertEquals(BasicType.STRING, variableNameToVariableType.get("ApexController.AccountName")); + assertEquals(BasicType.APEX_OBJECT, variableNameToVariableType.get("ApexController.InnerController")); + assertEquals(BasicType.ID, variableNameToVariableType.get("ApexController.InnerController.InnerAccountIdProp")); + assertEquals(BasicType.ID, variableNameToVariableType.get("ApexController.InnerController.InnerAccountId")); + assertEquals(BasicType.STRING, variableNameToVariableType.get("ApexController.InnerController.InnerAccountName")); + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ExpressionTypeTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ExpressionTypeTest.java new file mode 100644 index 0000000000..f80d7d717a --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ExpressionTypeTest.java @@ -0,0 +1,38 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import apex.jorje.semantic.symbol.type.BasicType; + +public class ExpressionTypeTest { + @Test + public void testFromString() { + assertEquals(ExpressionType.AutoNumber, ExpressionType.fromString("AutoNumber")); + assertEquals(ExpressionType.AutoNumber, ExpressionType.fromString("autonumber")); + assertEquals(ExpressionType.Unknown, ExpressionType.fromString("")); + assertEquals(ExpressionType.Unknown, ExpressionType.fromString(null)); + } + + @Test + public void testFromBasicType() { + assertEquals(ExpressionType.Checkbox, ExpressionType.fromBasicType(BasicType.BOOLEAN)); + assertEquals(ExpressionType.Number, ExpressionType.fromBasicType(BasicType.DECIMAL)); + assertEquals(ExpressionType.Number, ExpressionType.fromBasicType(BasicType.DOUBLE)); + assertEquals(ExpressionType.Unknown, ExpressionType.fromBasicType(BasicType.APEX_OBJECT)); + assertEquals(ExpressionType.Unknown, ExpressionType.fromBasicType(null)); + } + + @Test + public void testRequiresEncoding() { + assertFalse(ExpressionType.AutoNumber.requiresEscaping); + assertTrue(ExpressionType.Text.requiresEscaping); + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java new file mode 100644 index 0000000000..0d4e308525 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java @@ -0,0 +1,117 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Test; + +public class ObjectFieldTypesTest { + + /** + * Verify that CustomFields stored in sfdx project format are correctly parsed + */ + @Test + public void testSfdxAccountIsProperlyParsed() { + Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "StandarAccountPage.page")) + .toAbsolutePath(); + + ObjectFieldTypes objectFieldTypes = new ObjectFieldTypes(); + validateSfdxAccount(objectFieldTypes, vfPagePath, VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES); + } + + /** + * Verify that CustomFields stored in mdapi format are correctly parsed + */ + @Test + public void testMdapiAccountIsProperlyParsed() { + Path vfPagePath = VFTestContstants.MDAPI_PATH.resolve(Paths.get("pages", "StandarAccountPage.page")) + .toAbsolutePath(); + + ObjectFieldTypes objectFieldTypes = new ObjectFieldTypes(); + validateMDAPIAccount(objectFieldTypes, vfPagePath, VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES); + } + + /** + * Verify that fields are found across multiple directories + */ + @Test + public void testFieldsAreFoundInMultipleDirectories() { + ObjectFieldTypes objectFieldTypes; + Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "StandarAccountPage.page")) + .toAbsolutePath(); + + List paths = Arrays.asList(VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES.get(0), + VFTestContstants.ABSOLUTE_MDAPI_OBJECTS_DIRECTORIES.get(0)); + objectFieldTypes = new ObjectFieldTypes(); + validateSfdxAccount(objectFieldTypes, vfPagePath, paths); + validateMDAPIAccount(objectFieldTypes, vfPagePath, paths); + + Collections.reverse(paths); + objectFieldTypes = new ObjectFieldTypes(); + validateSfdxAccount(objectFieldTypes, vfPagePath, paths); + validateMDAPIAccount(objectFieldTypes, vfPagePath, paths); + } + + @Test + public void testInvalidDirectoryDoesNotCauseAnException() { + Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "StandarAccountPage.page")) + .toAbsolutePath(); + String vfFileName = vfPagePath.toString(); + + List paths = Arrays.asList(Paths.get("..", "objects-does-not-exist").toString()); + ObjectFieldTypes objectFieldTypes = new ObjectFieldTypes(); + assertNull(objectFieldTypes.getVariableType("Account.DoesNotExist__c", vfFileName, paths)); + } + + /** + * Validate the expected results when the Account Fields are stored in decomposed sfdx format + */ + private void validateSfdxAccount(ObjectFieldTypes objectFieldTypes, Path vfPagePath, List paths) { + String vfFileName = vfPagePath.toString(); + + assertEquals(ExpressionType.Checkbox, + objectFieldTypes.getVariableType("Account.Checkbox__c", vfFileName, paths)); + assertEquals(ExpressionType.DateTime, + objectFieldTypes.getVariableType("Account.DateTime__c", vfFileName, paths)); + assertEquals(ExpressionType.LongTextArea, + objectFieldTypes.getVariableType("Account.LongTextArea__c", vfFileName, paths)); + assertEquals(ExpressionType.Picklist, + objectFieldTypes.getVariableType("Account.Picklist__c", vfFileName, paths)); + assertEquals(ExpressionType.Text, + objectFieldTypes.getVariableType("Account.Text__c", vfFileName, paths)); + assertEquals(ExpressionType.TextArea, + objectFieldTypes.getVariableType("Account.TextArea__c", vfFileName, paths)); + assertNull(objectFieldTypes.getVariableType("Account.DoesNotExist__c", vfFileName, paths)); + } + + /** + * Validate the expected results when the Account Fields are stored in a single file MDAPI format + */ + private void validateMDAPIAccount(ObjectFieldTypes objectFieldTypes, Path vfPagePath, List paths) { + String vfFileName = vfPagePath.toString(); + + assertEquals(ExpressionType.Checkbox, + objectFieldTypes.getVariableType("Account.MDCheckbox__c", vfFileName, paths)); + assertEquals(ExpressionType.DateTime, + objectFieldTypes.getVariableType("Account.MDDateTime__c", vfFileName, paths)); + assertEquals(ExpressionType.LongTextArea, + objectFieldTypes.getVariableType("Account.MDLongTextArea__c", vfFileName, paths)); + assertEquals(ExpressionType.Picklist, + objectFieldTypes.getVariableType("Account.MDPicklist__c", vfFileName, paths)); + assertEquals(ExpressionType.Text, + objectFieldTypes.getVariableType("Account.MDText__c", vfFileName, paths)); + assertEquals(ExpressionType.TextArea, + objectFieldTypes.getVariableType("Account.MDTextArea__c", vfFileName, paths)); + assertNull(objectFieldTypes.getVariableType("Account.DoesNotExist__c", vfFileName, paths)); + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java index f478885c12..9ae272d088 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java @@ -5,7 +5,13 @@ package net.sourceforge.pmd.lang.vf; import net.sourceforge.pmd.AbstractRuleSetFactoryTest; +import net.sourceforge.pmd.lang.apex.rule.ApexXPathRule; public class RuleSetFactoryTest extends AbstractRuleSetFactoryTest { - // no additional tests + public RuleSetFactoryTest() { + super(); + // Copied from net.sourceforge.pmd.lang.apex.RuleSetFactoryTest + // Apex rules are found in the classpath because this module has a dependency on pmd-apex + validXPathClassNames.add(ApexXPathRule.class.getName()); + } } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestContstants.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestContstants.java new file mode 100644 index 0000000000..5469f26389 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestContstants.java @@ -0,0 +1,25 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; + +public abstract class VFTestContstants { + private static final Path ROOT_PATH = Paths.get("src", "test", "resources", "net", "sourceforge", + "pmd", "lang", "vf").toAbsolutePath(); + + public static final Path SFDX_PATH = ROOT_PATH.resolve("metadata-sfdx"); + + public static final Path MDAPI_PATH = ROOT_PATH.resolve("metadata-mdapi"); + public static final List ABSOLUTE_MDAPI_OBJECTS_DIRECTORIES = + Collections.singletonList(MDAPI_PATH.resolve("objects").toAbsolutePath().toString()); + + public static final List RELATIVE_APEX_DIRECTORIES = Collections.singletonList(".." + File.separator + "classes"); + public static final List RELATIVE_OBJECTS_DIRECTORIES = Collections.singletonList(".." + File.separator + "objects"); +} 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 40212b810e..22af978c1b 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,8 +4,162 @@ package net.sourceforge.pmd.lang.vf.rule.security; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import org.junit.Test; + +import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.PMDException; +import net.sourceforge.pmd.Report; +import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.RuleSet; +import net.sourceforge.pmd.RuleSets; +import net.sourceforge.pmd.RuleViolation; +import net.sourceforge.pmd.RulesetsFactoryUtils; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.Parser; +import net.sourceforge.pmd.lang.ParserOptions; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.vf.VFTestContstants; +import net.sourceforge.pmd.lang.vf.VfLanguageModule; +import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.testframework.PmdRuleTst; public class VfUnescapeElTest extends PmdRuleTst { - // no additional unit tests + public static final String EXPECTED_RULE_MESSAGE = "Avoid unescaped user controlled content in EL"; + + /** + * Verify that CustomFields stored in sfdx project format are correctly parsed + */ + @Test + public void testSfdxCustomFields() throws IOException, PMDException { + Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "StandardAccount.page")).toAbsolutePath(); + + Report report = runRule(vfPagePath, VFTestContstants.RELATIVE_APEX_DIRECTORIES, + VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES); + List ruleViolations = report.getViolations(); + assertEquals(6, ruleViolations.size()); + int firstLineWithErrors = 7; + for (int i = 0; i < ruleViolations.size(); i++) { + RuleViolation ruleViolation = ruleViolations.get(i); + assertEquals(EXPECTED_RULE_MESSAGE, ruleViolation.getDescription()); + assertEquals(firstLineWithErrors + i, ruleViolation.getBeginLine()); + } + } + + /** + * Verify that CustomFields stored in mdapi format are correctly parsed + */ + @Test + public void testMdapiCustomFields() throws IOException, PMDException { + Path vfPagePath = VFTestContstants.MDAPI_PATH.resolve(Paths.get("pages", "StandardAccount.page")).toAbsolutePath(); + + Report report = runRule(vfPagePath, VFTestContstants.RELATIVE_APEX_DIRECTORIES, + VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES); + List ruleViolations = report.getViolations(); + assertEquals(6, ruleViolations.size()); + int firstLineWithErrors = 8; + for (int i = 0; i < ruleViolations.size(); i++) { + RuleViolation ruleViolation = ruleViolations.get(i); + assertEquals(EXPECTED_RULE_MESSAGE, ruleViolation.getDescription()); + assertEquals(firstLineWithErrors + i, ruleViolation.getBeginLine()); + } + } + + /** + * Tests a page with a single Apex controller + */ + @Test + public void testApexController() throws IOException, PMDException { + Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "ApexController.page")).toAbsolutePath(); + + Report report = runRule(vfPagePath, VFTestContstants.RELATIVE_APEX_DIRECTORIES, + VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES); + List ruleViolations = report.getViolations(); + assertEquals(2, ruleViolations.size()); + int firstLineWithErrors = 9; + for (int i = 0; i < ruleViolations.size(); i++) { + // There should start at line 9 + RuleViolation ruleViolation = ruleViolations.get(i); + assertEquals(EXPECTED_RULE_MESSAGE, ruleViolation.getDescription()); + assertEquals(firstLineWithErrors + i, ruleViolation.getBeginLine()); + } + } + + /** + * Tests a page with a standard controller and two Apex extensions + */ + @Test + public void testExtensions() throws IOException, PMDException { + Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "StandardAccountWithExtensions.page")).toAbsolutePath(); + + Report report = runRule(vfPagePath, VFTestContstants.RELATIVE_APEX_DIRECTORIES, + VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES); + List ruleViolations = report.getViolations(); + assertEquals(8, ruleViolations.size()); + int firstLineWithErrors = 9; + for (int i = 0; i < ruleViolations.size(); i++) { + RuleViolation ruleViolation = ruleViolations.get(i); + assertEquals(EXPECTED_RULE_MESSAGE, ruleViolation.getDescription()); + assertEquals(firstLineWithErrors + i, ruleViolation.getBeginLine()); + } + } + + /** + * Runs a rule against a Visualforce page on the file system. This code is based on + * {@link net.sourceforge.pmd.testframework.RuleTst#runTestFromString(String, Rule, Report, LanguageVersion, boolean)} + */ + private Report runRule(Path vfPagePath, List apexDirectories, List objectsDirectories) throws FileNotFoundException, PMDException { + LanguageVersion languageVersion = LanguageRegistry.getLanguage(VfLanguageModule.NAME).getDefaultVersion(); + ParserOptions parserOptions = languageVersion.getLanguageVersionHandler().getDefaultParserOptions(); + Parser parser = languageVersion.getLanguageVersionHandler().getParser(parserOptions); + + Node node = parser.parse(vfPagePath.toString(), new FileReader(vfPagePath.toFile())); + assertNotNull(node); + + // BEGIN Based on RuleTst class + PMD p = new PMD(); + p.getConfiguration().setDefaultLanguageVersion(languageVersion); + p.getConfiguration().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. + p.getConfiguration().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); + } + }); + + Rule rule = findRule("category/vf/security.xml", "VfUnescapeEl"); + PropertyDescriptor apexPropertyDescriptor = rule.getPropertyDescriptor("apexDirectories"); + PropertyDescriptor objectPropertyDescriptor = rule.getPropertyDescriptor("objectsDirectories"); + rule.setProperty(apexPropertyDescriptor, apexDirectories); + rule.setProperty(objectPropertyDescriptor, objectsDirectories); + Report report = new Report(); + RuleContext ctx = new RuleContext(); + ctx.setReport(report); + ctx.setSourceCodeFile(vfPagePath.toFile()); + ctx.setLanguageVersion(languageVersion); + ctx.setIgnoreExceptions(false); + RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); + p.getSourceCodeProcessor().processSourceCode(new FileReader(vfPagePath.toFile()), new RuleSets(rules), ctx); + // END Based on RuleTst class + + return report; + } } diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-mdapi/objects/Account.object b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-mdapi/objects/Account.object new file mode 100644 index 0000000000..5c37ef3c93 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-mdapi/objects/Account.object @@ -0,0 +1,445 @@ + + + + CallHighlightAction + Default + + + CallHighlightAction + Large + Default + + + CallHighlightAction + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + EmailHighlightAction + Default + + + EmailHighlightAction + Large + Default + + + EmailHighlightAction + Small + Default + + + EnableCustomerPortalUser + Default + + + EnableCustomerPortalUser + Large + Default + + + EnableCustomerPortalUser + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + ListClean + Default + + + ListClean + Large + Default + + + ListClean + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + RequestUpdate + Default + + + RequestUpdate + Large + Default + + + RequestUpdate + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + SmsHighlightAction + Default + + + SmsHighlightAction + Large + Default + + + SmsHighlightAction + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + + ViewCustomerPortalUser + Default + + + ViewCustomerPortalUser + Large + Default + + + ViewCustomerPortalUser + Small + Default + + + WebsiteHighlightAction + Default + + + WebsiteHighlightAction + Large + Default + + + WebsiteHighlightAction + Small + Default + + SYSTEM + true + false + Private + + ACCOUNT.NAME + ACCOUNT.ADDRESS1_CITY + ACCOUNT.PHONE1 + ACCOUNT.NAME + ACCOUNT.SITE + CORE.USERS.ALIAS + ACCOUNT.TYPE + ACCOUNT.NAME + ACCOUNT.SITE + CORE.USERS.ALIAS + ACCOUNT.TYPE + ACCOUNT.PHONE1 + ACCOUNT.NAME + ACCOUNT.SITE + ACCOUNT.PHONE1 + CORE.USERS.ALIAS + + ReadWrite + + AccountNumber + false + + + AccountSource + false + Picklist + + + AnnualRevenue + false + + + BillingAddress + false + + + MDCheckbox__c + false + false + + false + Checkbox + + + CleanStatus + false + + + DandbCompanyId + false + Lookup + + + MDDateTime__c + false + + false + false + DateTime + + + Description + false + + + DunsNumber + false + + + Fax + false + + + Industry + false + Picklist + + + Jigsaw + false + + + NaicsCode + false + + + NaicsDesc + false + + + Name + true + + + NumberOfEmployees + false + + + OperatingHoursId + false + Lookup + + + OwnerId + true + Lookup + + + Ownership + false + Picklist + + + ParentId + false + Hierarchy + + + Phone + false + + + MDPicklist__c + false + + false + false + Picklist + + + false + + Value1 + false + + + + Value2 + false + + + + + + + Rating + false + Picklist + + + ShippingAddress + false + + + Sic + false + + + SicDesc + false + + + Site + false + + + MDLongTextArea__c + false + + 32768 + false + LongTextArea + 3 + + + MDTextArea__c + false + + false + false + TextArea + + + MDText__c + false + + 255 + false + false + Text + false + + + TickerSymbol + false + + + Tradestyle + false + + + Type + false + Picklist + + + Website + false + + + YearStarted + false + + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-mdapi/pages/StandardAccount.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-mdapi/pages/StandardAccount.page new file mode 100644 index 0000000000..628375b2d2 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-mdapi/pages/StandardAccount.page @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexController.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexController.cls new file mode 100644 index 0000000000..df449e3e6f --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexController.cls @@ -0,0 +1,39 @@ +public class ApexController { + public Id AccountIdProp { get; set; } + + public ApexController() { + acc = [SELECT Id, Name, Site FROM Account + WHERE Id = :ApexPages.currentPage().getParameters().get('id')]; + this.AccountIdProp = acc.Id; + } + + public Id getAccountId() { + return acc.id; + } + + public String getAccountName() { + return acc.name; + } + + public InnerController getInnerController() { + return new InnerController(this); + } + + public class InnerController { + private ApexController parent; + public Id InnerAccountIdProp { get; set; } + + public InnerController(ApexController parent) { + this.parent = parent; + this.InnerAccountIdProp = parent.AccountIdProp; + } + + public Id getInnerAccountId() { + return 'Inner: ' + parent.acc.id; + } + + public String getInnerAccountName() { + return 'Inner: ' + parent.acc.name; + } + } +} \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexExtension1.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexExtension1.cls new file mode 100644 index 0000000000..764474cb46 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexExtension1.cls @@ -0,0 +1,4 @@ +public class ApexExtension1 { + public String StringFromExtension1 {get; set;} + public Id IdFromExtension1 {get; set;} +} diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexExtension2.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexExtension2.cls new file mode 100644 index 0000000000..6f6ff84d65 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexExtension2.cls @@ -0,0 +1,4 @@ +public class ApexExtension2 { + public String StringFromExtension2 {get; set;} + public Id IdFromExtension2 {get; set;} +} diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexWithConflictingPropertyTypes.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexWithConflictingPropertyTypes.cls new file mode 100644 index 0000000000..f166301e47 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexWithConflictingPropertyTypes.cls @@ -0,0 +1,7 @@ +public class ApexWithConflictingPropertyTypes { + public String ConflictingProp { get; set; } + + public Integer getConflictingProp() { + return ''; + } +} \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Checkbox__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Checkbox__c.field-meta.xml new file mode 100644 index 0000000000..03ffc46229 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Checkbox__c.field-meta.xml @@ -0,0 +1,9 @@ + + + Checkbox__c + false + false + + false + Checkbox + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/DateTime__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/DateTime__c.field-meta.xml new file mode 100644 index 0000000000..5e0ce1cd2c --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/DateTime__c.field-meta.xml @@ -0,0 +1,9 @@ + + + DateTime__c + false + + false + false + DateTime + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml new file mode 100644 index 0000000000..6de4650b1f --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml @@ -0,0 +1,10 @@ + + + LongTextArea__c + false + + 32768 + false + LongTextArea + 3 + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Picklist__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Picklist__c.field-meta.xml new file mode 100644 index 0000000000..597d9d3d30 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Picklist__c.field-meta.xml @@ -0,0 +1,24 @@ + + + Picklist__c + false + + false + false + Picklist + + + false + + Value1 + false + + + + Value2 + false + + + + + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/TextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/TextArea__c.field-meta.xml new file mode 100644 index 0000000000..cd59083b9e --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/TextArea__c.field-meta.xml @@ -0,0 +1,9 @@ + + + TextArea__c + false + + false + false + TextArea + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Text__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Text__c.field-meta.xml new file mode 100644 index 0000000000..efeeb5262a --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Text__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Text__c + false + + 255 + false + false + Text + false + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/ApexController.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/ApexController.page new file mode 100644 index 0000000000..2939c23f16 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/ApexController.page @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/StandardAccount.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/StandardAccount.page new file mode 100644 index 0000000000..d470c4f5f6 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/StandardAccount.page @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/StandardAccountWithExtensions.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/StandardAccountWithExtensions.page new file mode 100644 index 0000000000..aef9bd6abf --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/StandardAccountWithExtensions.page @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file From ef9e350fd53e3f6f00da34659ec8433ce398f9b6 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Thu, 22 Oct 2020 12:11:44 -0700 Subject: [PATCH 03/98] Mark AbstractVfTypedElExpressionRule as abstract --- .../lang/vf/rule/security/AbstractVfTypedElExpressionRule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java index 7a68a387d8..3c781ffc86 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java @@ -23,7 +23,7 @@ import net.sourceforge.pmd.properties.PropertyFactory; * Represents a rule where the {@link net.sourceforge.pmd.lang.vf.ast.ASTIdentifier} nodes are enhanced with the * node's {@link ExpressionType}. This is achieved by processing metadata files referenced by the Visualforce page. */ -class AbstractVfTypedElExpressionRule extends AbstractVfRule { +abstract class AbstractVfTypedElExpressionRule extends AbstractVfRule { /** * Directory that contains Apex classes that may be referenced from a Visualforce page. */ From 412d39f51384468810e08ffe11ac218138fb2395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 7 Jul 2020 16:09:14 +0200 Subject: [PATCH 04/98] Replace createFactory methods with a builder Deprecate compatibility filter Deprecate methods in RulesetsFactUtils --- .../pmd/lang/apex/DefaultRulesetTest.java | 5 +- .../main/java/net/sourceforge/pmd/PMD.java | 6 +- .../net/sourceforge/pmd/RuleSetFactory.java | 131 +++++++++++++++--- .../pmd/RuleSetFactoryCompatibility.java | 8 ++ .../sourceforge/pmd/RulesetsFactoryUtils.java | 18 ++- .../pmd/ant/internal/PMDTaskImpl.java | 14 +- .../sourceforge/pmd/RuleSetFactoryTest.java | 30 ++-- .../pmd/lang/java/PMD5RulesetTest.java | 5 +- .../pmd/lang/java/QuickstartRulesetTest.java | 5 +- 9 files changed, 167 insertions(+), 55 deletions(-) diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java index 11ef33e0d1..7cf90ce8f6 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java @@ -14,16 +14,15 @@ import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemErrRule; -import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RulesetsFactoryUtils; +import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; public class DefaultRulesetTest { @Rule public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests(); - private RuleSetFactory factory = RulesetsFactoryUtils.createFactory(RulePriority.LOW, true, false); + private RuleSetFactory factory = new RuleSetFactoryConfig().enableCompatibility(false).createFactory(); @Test public void loadDefaultRuleset() throws Exception { 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 066d2fc935..5a4b864375 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -21,6 +21,7 @@ import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; +import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; import net.sourceforge.pmd.benchmark.TextTimingReportRenderer; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; @@ -45,7 +46,6 @@ import net.sourceforge.pmd.stat.Metric; import net.sourceforge.pmd.util.ClasspathClassLoader; import net.sourceforge.pmd.util.FileUtil; import net.sourceforge.pmd.util.IOUtil; -import net.sourceforge.pmd.util.ResourceLoader; import net.sourceforge.pmd.util.database.DBMSMetadata; import net.sourceforge.pmd.util.database.DBURI; import net.sourceforge.pmd.util.database.SourceObject; @@ -203,7 +203,7 @@ public class PMD { public static int doPMD(PMDConfiguration configuration) { // Load the RuleSets - final RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.getRulesetFactory(configuration, new ResourceLoader()); + final RuleSetFactory ruleSetFactory = RuleSetFactoryConfig.fromPmdConfig(configuration).createFactory(); final RuleSets ruleSets = RulesetsFactoryUtils.getRuleSetsWithBenchmark(configuration.getRuleSets(), ruleSetFactory); if (ruleSets == null) { return PMDCommandLineInterface.NO_ERRORS_STATUS; @@ -305,7 +305,7 @@ public class PMD { // Make sure the cache is listening for analysis results ctx.getReport().addListener(configuration.getAnalysisCache()); - final RuleSetFactory silentFactory = new RuleSetFactory(ruleSetFactory, false); + final RuleSetFactory silentFactory = ruleSetFactory.toConfig().warnDeprecated(false).createFactory(); newFileProcessor(configuration).processFiles(silentFactory, files, ctx, renderers); configuration.getAnalysisCache().persist(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 3fef741d9a..e248c3ee86 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -44,11 +44,9 @@ import net.sourceforge.pmd.util.ResourceLoader; /** * RuleSetFactory is responsible for creating RuleSet instances from XML - * content. By default Rules will be loaded using the {@link RulePriority#LOW} priority, - * with Rule deprecation warnings off; - * the ruleset compatibility filter is active, too (see {@link RuleSetFactoryCompatibility}); - * if the ruleset contains rule references (e.g. for renamed or moved rules), these - * are ignored by default. + * content. See {@link RuleSetFactoryConfig} for configuration options and + * their defaults. The constructors of this class are deprecated and their + * usages should be replaced by use of {@link RuleSetFactoryConfig#createFactory()}. */ public class RuleSetFactory { @@ -67,7 +65,7 @@ public class RuleSetFactory { private final Map parsedRulesets = new HashMap<>(); /** - * @deprecated Use {@link RulesetsFactoryUtils#defaultFactory()} + * @deprecated Use a {@link RuleSetFactoryConfig} to build a new factory */ @Deprecated // to be removed with PMD 7.0.0. public RuleSetFactory() { @@ -75,8 +73,7 @@ public class RuleSetFactory { } /** - * @deprecated Use {@link RulesetsFactoryUtils#createFactory(ClassLoader, RulePriority, boolean, boolean)} - * or {@link RulesetsFactoryUtils#createFactory(RulePriority, boolean, boolean)} + * @deprecated Use a {@link RuleSetFactoryConfig} to build a new factory */ @Deprecated // to be removed with PMD 7.0.0. public RuleSetFactory(final ClassLoader classLoader, final RulePriority minimumPriority, @@ -85,8 +82,7 @@ public class RuleSetFactory { } /** - * @deprecated Use {@link RulesetsFactoryUtils#createFactory(ClassLoader, RulePriority, boolean, boolean)} - * or {@link RulesetsFactoryUtils#createFactory(RulePriority, boolean, boolean)} + * @deprecated Use a {@link RuleSetFactoryConfig} to build a new factory */ @Deprecated // to be hidden with PMD 7.0.0. public RuleSetFactory(final ResourceLoader resourceLoader, final RulePriority minimumPriority, @@ -115,12 +111,20 @@ public class RuleSetFactory { * The factory whose configuration to copy. * @param warnDeprecated * Whether deprecation warnings are to be produced by this - * factory. + * factory + * + * @deprecated Use {@link #toConfig()} to rebuild a factory from a configuration */ + @Deprecated public RuleSetFactory(final RuleSetFactory factory, final boolean warnDeprecated) { this(factory.resourceLoader, factory.minimumPriority, warnDeprecated, factory.compatibilityFilter != null); } + + RuleSetFactory(RuleSetFactoryConfig config) { + this(config.resourceLoader, config.minimumPriority, config.warnDeprecated, config.enableCompatibility, config.includeDeprecatedRuleReferences); + } + /** * Gets the compatibility filter in order to adjust it, e.g. add additional * filters. @@ -154,10 +158,9 @@ public class RuleSetFactory { } catch (RuleSetNotFoundException e) { LOG.warning("The language " + language.getTerseName() + " provides no " + rulesetsProperties + "."); } catch (IOException ioe) { - throw new RuntimeException("Couldn't find " + rulesetsProperties - + "; please ensure that the directory is on the classpath. The current classpath is: " - + System.getProperty("java.class.path")); - } + throw new RuntimeException("Couldn't find " + rulesetsProperties + + "; please ensure that the directory is on the classpath. The current classpath is: " + + System.getProperty("java.class.path"));} } return createRuleSets(ruleSetReferenceIds).getRuleSetsIterator(); } @@ -348,7 +351,8 @@ public class RuleSetFactory { private Rule createRule(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) throws RuleSetNotFoundException { if (ruleSetReferenceId.isAllRules()) { - throw new IllegalArgumentException("Cannot parse a single Rule from an all Rule RuleSet reference: <" + ruleSetReferenceId + ">."); + throw new IllegalArgumentException( + "Cannot parse a single Rule from an all Rule RuleSet reference: <" + ruleSetReferenceId + ">."); } RuleSet ruleSet; // java8: computeIfAbsent @@ -557,7 +561,7 @@ public class RuleSetFactory { // load the ruleset with minimum priority low, so that we get all rules, to be able to exclude any rule // minimum priority will be applied again, before constructing the final ruleset - RuleSetFactory ruleSetFactory = new RuleSetFactory(resourceLoader, RulePriority.LOW, false, this.compatibilityFilter != null); + RuleSetFactory ruleSetFactory = toConfig().filterAbovePriority(RulePriority.LOW).warnDeprecated(false).createFactory(); RuleSet otherRuleSet = ruleSetFactory.createRuleSet(RuleSetReferenceId.parse(ref).get(0)); List potentialRules = new ArrayList<>(); int countDeprecated = 0; @@ -672,7 +676,7 @@ public class RuleSetFactory { // load the ruleset with minimum priority low, so that we get all rules, to be able to exclude any rule // minimum priority will be applied again, before constructing the final ruleset - RuleSetFactory ruleSetFactory = new RuleSetFactory(resourceLoader, RulePriority.LOW, false, this.compatibilityFilter != null); + RuleSetFactory ruleSetFactory = toConfig().warnDeprecated(false).createFactory(); boolean isSameRuleSet = false; RuleSetReferenceId otherRuleSetReferenceId = RuleSetReferenceId.parse(ref).get(0); @@ -832,4 +836,95 @@ public class RuleSetFactory { return false; } } + + + public RuleSetFactoryConfig toConfig() { + return new RuleSetFactoryConfig().loadResourcesWith(resourceLoader) + .filterAbovePriority(minimumPriority) + .warnDeprecated(warnDeprecated) + .enableCompatibility(compatibilityFilter != null); + } + + + /** + * Configuration of a {@link RuleSetFactory}. This is a fluent builder + * pattern. Use this instead of the constructors of RuleSetFactory. + */ + public static final class RuleSetFactoryConfig { + + ResourceLoader resourceLoader = new ResourceLoader(RuleSetFactoryConfig.class.getClassLoader()); + RulePriority minimumPriority = RulePriority.LOW; + boolean warnDeprecated = true; + boolean enableCompatibility = true; + boolean includeDeprecatedRuleReferences = false; + + /** + * Specify that the given classloader should be used to resolve + * paths to external ruleset references. The default uses PMD's + * own classpath. + */ + public RuleSetFactoryConfig loadResourcesWith(ClassLoader classLoader) { + this.resourceLoader = new ResourceLoader(classLoader); + return this; + } + + // internal + RuleSetFactoryConfig loadResourcesWith(ResourceLoader loader) { + this.resourceLoader = loader; + return this; + } + + /** + * Filter loaded rules to only those that match or are above + * the given priority. The default is {@link RulePriority#LOW}, + * ie, no filtering occurs. + */ + public RuleSetFactoryConfig filterAbovePriority(RulePriority minimumPriority) { + this.minimumPriority = minimumPriority; + return this; + } + + /** + * Log a warning when referencing a deprecated rule. + * This is enabled by default. + */ + public RuleSetFactoryConfig warnDeprecated(boolean warn) { + this.warnDeprecated = warn; + return this; + } + + /** + * Enable translating old rule references to newer ones, if they have + * been moved or renamed. This is enabled by default, if disabled, + * unresolved references will not be translated and will produce an + * error. + */ + public RuleSetFactoryConfig enableCompatibility(boolean enable) { + this.enableCompatibility = enable; + return this; + } + + /** + * Follow deprecated rule references. By default this is off, + * and those references will be ignored (with a warning depending + * on {@link #enableCompatibility(boolean)}). + */ + public RuleSetFactoryConfig includeDeprecatedRuleReferences(boolean enable) { + this.includeDeprecatedRuleReferences = enable; + return this; + } + + public RuleSetFactory createFactory() { + return new RuleSetFactory(this); + } + + /** + * Configure a new ruleset factory builder according to the parameters + * of the given PMD configuration. + */ + public static RuleSetFactoryConfig fromPmdConfig(PMDConfiguration configuration) { + return new RuleSetFactoryConfig().filterAbovePriority(configuration.getMinimumPriority()) + .enableCompatibility(configuration.isRuleSetFactoryCompatibilityEnabled()); + } + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java index cd933218fd..a0008ab3b4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java @@ -18,13 +18,21 @@ import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; +import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; +import net.sourceforge.pmd.annotation.InternalApi; + /** * Provides a simple filter mechanism to avoid failing to parse an old ruleset, * which references rules, that have either been removed from PMD already or * renamed or moved to another ruleset. * * @see issue 1360 + * + * @deprecated Use {@link RuleSetFactoryConfig#enableCompatibility(boolean)} to enable this feature. + * This implementation is internal API. */ +@InternalApi +@Deprecated public class RuleSetFactoryCompatibility { private static final Logger LOG = Logger.getLogger(RuleSetFactoryCompatibility.class.getName()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java index 69a1ff066c..609c7b6549 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd; import java.util.logging.Level; import java.util.logging.Logger; +import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; @@ -76,7 +77,7 @@ public final class RulesetsFactoryUtils { } /** - * @deprecated Use {@link #createFactory(PMDConfiguration)} or {@link #createFactory(PMDConfiguration, ClassLoader)} + * @deprecated Use a {@link RuleSetFactoryConfig} */ @InternalApi @Deprecated @@ -96,7 +97,10 @@ public final class RulesetsFactoryUtils { * @return A ruleset factory * * @see #createFactory(PMDConfiguration, ClassLoader) + * + * @deprecated Use {@link RuleSetFactoryConfig#fromPmdConfig(PMDConfiguration)} */ + @Deprecated public static RuleSetFactory createFactory(final PMDConfiguration configuration) { return createFactory(configuration, RulesetsFactoryUtils.class.getClassLoader()); } @@ -107,7 +111,7 @@ public final class RulesetsFactoryUtils { * * @return A ruleset factory * - * @see #createFactory(PMDConfiguration, ClassLoader) + * @see RuleSetFactoryConfig */ public static RuleSetFactory defaultFactory() { return new RuleSetFactory(); @@ -124,7 +128,10 @@ public final class RulesetsFactoryUtils { * @return A ruleset factory * * @see #createFactory(PMDConfiguration) + * + * @deprecated Use a {@link RuleSetFactoryConfig} */ + @Deprecated public static RuleSetFactory createFactory(final PMDConfiguration configuration, ClassLoader classLoader) { return createFactory(classLoader, configuration.getMinimumPriority(), @@ -145,7 +152,10 @@ public final class RulesetsFactoryUtils { * @return A ruleset factory * * @see #createFactory(PMDConfiguration) + * + * @deprecated Use a {@link RuleSetFactoryConfig} */ + @Deprecated public static RuleSetFactory createFactory(ClassLoader classLoader, RulePriority minimumPriority, boolean warnDeprecated, @@ -166,11 +176,13 @@ public final class RulesetsFactoryUtils { * @return A ruleset factory * * @see #createFactory(PMDConfiguration) + * + * @deprecated Use a {@link RuleSetFactoryConfig} */ + @Deprecated public static RuleSetFactory createFactory(RulePriority minimumPriority, boolean warnDeprecated, boolean enableCompatibility) { - return new RuleSetFactory(new ResourceLoader(), minimumPriority, warnDeprecated, enableCompatibility); } 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 f78d78b8b9..0abe673de8 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 @@ -29,9 +29,9 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; +import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; import net.sourceforge.pmd.RuleSetNotFoundException; import net.sourceforge.pmd.RuleSets; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.ant.Formatter; import net.sourceforge.pmd.ant.PMDTask; import net.sourceforge.pmd.ant.SourceLanguage; @@ -41,7 +41,6 @@ import net.sourceforge.pmd.renderers.AbstractRenderer; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.util.ClasspathClassLoader; import net.sourceforge.pmd.util.IOUtil; -import net.sourceforge.pmd.util.ResourceLoader; import net.sourceforge.pmd.util.datasource.DataSource; import net.sourceforge.pmd.util.datasource.FileDataSource; import net.sourceforge.pmd.util.log.AntLogHandler; @@ -103,8 +102,9 @@ public class PMDTaskImpl { setupClassLoader(); // Setup RuleSetFactory and validate RuleSets - final ResourceLoader rl = setupResourceLoader(); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.getRulesetFactory(configuration, rl); + RuleSetFactory ruleSetFactory = RuleSetFactoryConfig.fromPmdConfig(configuration) + .loadResourcesWith(setupResourceLoader()) + .createFactory(); try { // This is just used to validate and display rules. Each thread will create its own ruleset @@ -216,7 +216,7 @@ public class PMDTaskImpl { } } - private ResourceLoader setupResourceLoader() { + private ClassLoader setupResourceLoader() { if (classpath == null) { classpath = new Path(project); } @@ -233,8 +233,8 @@ public class PMDTaskImpl { // are loaded twice // and exist in multiple class loaders final boolean parentFirst = true; - return new ResourceLoader(new AntClassLoader(Thread.currentThread().getContextClassLoader(), - project, classpath, parentFirst)); + return new AntClassLoader(Thread.currentThread().getContextClassLoader(), + project, classpath, parentFirst); } private void handleError(RuleContext ctx, Report errorReport, RuntimeException pmde) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 1732c1f732..840def81e1 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -23,6 +23,7 @@ import org.apache.commons.lang3.StringUtils; import org.junit.Test; import org.junit.rules.ExpectedException; +import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; import net.sourceforge.pmd.junit.JavaUtilLoggingRule; import net.sourceforge.pmd.junit.LocaleRule; import net.sourceforge.pmd.lang.DummyLanguageModule; @@ -505,40 +506,40 @@ public class RuleSetFactoryTest { @Test public void testReferencePriority() throws RuleSetNotFoundException { - ResourceLoader rl = new ResourceLoader(); - RuleSetFactory rsf = new RuleSetFactory(rl, RulePriority.LOW, false, true); + RuleSetFactoryConfig config = new RuleSetFactoryConfig().warnDeprecated(false).enableCompatibility(true); + RuleSetFactory rsf = config.filterAbovePriority(RulePriority.LOW).createFactory(); RuleSet ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); assertEquals("Number of Rules", 3, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("MockRuleName")); assertNotNull(ruleSet.getRuleByName("MockRuleNameRef")); assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - rsf = new RuleSetFactory(rl, RulePriority.MEDIUM_HIGH, false, true); + rsf = config.filterAbovePriority(RulePriority.MEDIUM_HIGH).createFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); assertEquals("Number of Rules", 2, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("MockRuleNameRef")); assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - rsf = new RuleSetFactory(rl, RulePriority.HIGH, false, true); + rsf = config.filterAbovePriority(RulePriority.HIGH).createFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); assertEquals("Number of Rules", 1, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - rsf = new RuleSetFactory(rl, RulePriority.LOW, false, true); + rsf = config.filterAbovePriority(RulePriority.LOW).createFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN)); assertEquals("Number of Rules", 3, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleName")); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRef")); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); - rsf = new RuleSetFactory(rl, RulePriority.MEDIUM_HIGH, false, true); + rsf = config.filterAbovePriority(RulePriority.MEDIUM_HIGH).createFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN)); assertEquals("Number of Rules", 2, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRef")); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); - rsf = new RuleSetFactory(rl, RulePriority.HIGH, false, true); + rsf = config.filterAbovePriority(RulePriority.HIGH).createFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN)); assertEquals("Number of Rules", 1, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); @@ -546,7 +547,7 @@ public class RuleSetFactoryTest { @Test public void testOverridePriorityLoadWithMinimum() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetFactory(new ResourceLoader(), RulePriority.MEDIUM_LOW, true, true); + RuleSetFactory rsf = new RuleSetFactoryConfig().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true).createFactory(); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); // only one rule should remain, since we filter out the other rule by minimum priority assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -567,13 +568,13 @@ public class RuleSetFactoryTest { @Test public void testExcludeWithMinimumPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.HIGH, true, true); + RuleSetFactory rsf = new RuleSetFactoryConfig().filterAbovePriority(RulePriority.HIGH).createFactory(); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // no rules should be loaded assertEquals("Number of Rules", 0, ruleset.getRules().size()); // now, load with default minimum priority - rsf = RulesetsFactoryUtils.defaultFactory(); + rsf = new RuleSetFactoryConfig().filterAbovePriority(RulePriority.LOW).createFactory(); ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // only one rule, we have excluded one... assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -602,10 +603,9 @@ public class RuleSetFactoryTest { @Test public void testSetPriority() throws RuleSetNotFoundException { - ResourceLoader rl = new ResourceLoader(); - RuleSetFactory rsf = new RuleSetFactory(rl, RulePriority.MEDIUM_HIGH, false, true); + RuleSetFactory rsf = new RuleSetFactoryConfig().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false).createFactory(); assertEquals(0, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); - rsf = new RuleSetFactory(rl, RulePriority.MEDIUM_LOW, false, true); + rsf = new RuleSetFactoryConfig().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false).createFactory(); assertEquals(1, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); } @@ -780,7 +780,7 @@ public class RuleSetFactoryTest { + " Ruleset which references a empty ruleset\n" + "\n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetFactory(new ResourceLoader(), RulePriority.LOW, true, true); + RuleSetFactory ruleSetFactory = new RuleSetFactoryConfig().loadResourcesWith(new ResourceLoader()).filterAbovePriority(RulePriority.LOW).warnDeprecated(true).enableCompatibility(true).createFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet(ref); assertEquals(0, ruleset.getRules().size()); @@ -1285,7 +1285,7 @@ public class RuleSetFactoryTest { } private RuleSet loadRuleSetWithDeprecationWarnings(String ruleSetXml) throws RuleSetNotFoundException { - RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.LOW, true, false); + RuleSetFactory rsf = new RuleSetFactoryConfig().warnDeprecated(true).enableCompatibility(false).createFactory(); return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml)); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java index 3401199005..135294a762 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java @@ -7,16 +7,15 @@ package net.sourceforge.pmd.lang.java; import org.junit.Assert; import org.junit.Test; -import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.util.ResourceLoader; +import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; public class PMD5RulesetTest { @Test public void loadRuleset() throws Exception { - RuleSetFactory ruleSetFactory = new RuleSetFactory(new ResourceLoader(), RulePriority.LOW, true, true); + RuleSetFactory ruleSetFactory = new RuleSetFactoryConfig().createFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet("net/sourceforge/pmd/lang/java/pmd5ruleset.xml"); Assert.assertNotNull(ruleset); Assert.assertNull(ruleset.getRuleByName("GuardLogStatementJavaUtil")); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java index 816c98b918..fa3bb3f027 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java @@ -14,11 +14,10 @@ import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemErrRule; -import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; +import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.util.ResourceLoader; public class QuickstartRulesetTest { @@ -50,7 +49,7 @@ public class QuickstartRulesetTest { } }); - RuleSetFactory ruleSetFactory = new RuleSetFactory(new ResourceLoader(), RulePriority.LOW, true, false); + RuleSetFactory ruleSetFactory = new RuleSetFactoryConfig().enableCompatibility(false).createFactory(); RuleSet quickstart = ruleSetFactory.createRuleSet("rulesets/java/quickstart.xml"); Assert.assertFalse(quickstart.getRules().isEmpty()); } From ac864aa5294a9caa72827fd8400c8db1337c3897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 26 Oct 2020 19:29:19 +0100 Subject: [PATCH 05/98] Rename --- .../pmd/lang/apex/DefaultRulesetTest.java | 4 +- .../main/java/net/sourceforge/pmd/PMD.java | 3 +- .../net/sourceforge/pmd/RuleSetFactory.java | 103 ++---------------- .../pmd/RuleSetFactoryCompatibility.java | 3 +- .../sourceforge/pmd/RuleSetParserConfig.java | 96 ++++++++++++++++ .../sourceforge/pmd/RulesetsFactoryUtils.java | 13 +-- .../pmd/ant/internal/PMDTaskImpl.java | 8 +- .../sourceforge/pmd/RuleSetFactoryTest.java | 17 ++- .../pmd/lang/java/PMD5RulesetTest.java | 4 +- .../pmd/lang/java/QuickstartRulesetTest.java | 4 +- .../lang/scala/ast/ScalaParsingHelper.java | 4 +- 11 files changed, 135 insertions(+), 124 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParserConfig.java diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java index 7cf90ce8f6..c2d76f7d1f 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java @@ -16,13 +16,13 @@ import org.junit.contrib.java.lang.system.SystemErrRule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; +import net.sourceforge.pmd.RuleSetParserConfig; public class DefaultRulesetTest { @Rule public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests(); - private RuleSetFactory factory = new RuleSetFactoryConfig().enableCompatibility(false).createFactory(); + private RuleSetFactory factory = new RuleSetParserConfig().enableCompatibility(false).createFactory(); @Test public void loadDefaultRuleset() throws Exception { 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 5a4b864375..c7aaf5877e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -21,7 +21,6 @@ import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; -import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; import net.sourceforge.pmd.benchmark.TextTimingReportRenderer; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; @@ -203,7 +202,7 @@ public class PMD { public static int doPMD(PMDConfiguration configuration) { // Load the RuleSets - final RuleSetFactory ruleSetFactory = RuleSetFactoryConfig.fromPmdConfig(configuration).createFactory(); + final RuleSetFactory ruleSetFactory = RuleSetParserConfig.fromPmdConfig(configuration).createFactory(); final RuleSets ruleSets = RulesetsFactoryUtils.getRuleSetsWithBenchmark(configuration.getRuleSets(), ruleSetFactory); if (ruleSets == null) { return PMDCommandLineInterface.NO_ERRORS_STATUS; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index e248c3ee86..6163e96c89 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -44,9 +44,9 @@ import net.sourceforge.pmd.util.ResourceLoader; /** * RuleSetFactory is responsible for creating RuleSet instances from XML - * content. See {@link RuleSetFactoryConfig} for configuration options and + * content. See {@link RuleSetParserConfig} for configuration options and * their defaults. The constructors of this class are deprecated and their - * usages should be replaced by use of {@link RuleSetFactoryConfig#createFactory()}. + * usages should be replaced by use of {@link RuleSetParserConfig#createFactory()}. */ public class RuleSetFactory { @@ -65,7 +65,7 @@ public class RuleSetFactory { private final Map parsedRulesets = new HashMap<>(); /** - * @deprecated Use a {@link RuleSetFactoryConfig} to build a new factory + * @deprecated Use a {@link RuleSetParserConfig} to build a new factory */ @Deprecated // to be removed with PMD 7.0.0. public RuleSetFactory() { @@ -73,7 +73,7 @@ public class RuleSetFactory { } /** - * @deprecated Use a {@link RuleSetFactoryConfig} to build a new factory + * @deprecated Use a {@link RuleSetParserConfig} to build a new factory */ @Deprecated // to be removed with PMD 7.0.0. public RuleSetFactory(final ClassLoader classLoader, final RulePriority minimumPriority, @@ -82,7 +82,7 @@ public class RuleSetFactory { } /** - * @deprecated Use a {@link RuleSetFactoryConfig} to build a new factory + * @deprecated Use a {@link RuleSetParserConfig} to build a new factory */ @Deprecated // to be hidden with PMD 7.0.0. public RuleSetFactory(final ResourceLoader resourceLoader, final RulePriority minimumPriority, @@ -121,7 +121,7 @@ public class RuleSetFactory { } - RuleSetFactory(RuleSetFactoryConfig config) { + RuleSetFactory(RuleSetParserConfig config) { this(config.resourceLoader, config.minimumPriority, config.warnDeprecated, config.enableCompatibility, config.includeDeprecatedRuleReferences); } @@ -838,93 +838,12 @@ public class RuleSetFactory { } - public RuleSetFactoryConfig toConfig() { - return new RuleSetFactoryConfig().loadResourcesWith(resourceLoader) - .filterAbovePriority(minimumPriority) - .warnDeprecated(warnDeprecated) - .enableCompatibility(compatibilityFilter != null); + public RuleSetParserConfig toConfig() { + return new RuleSetParserConfig().loadResourcesWith(resourceLoader) + .filterAbovePriority(minimumPriority) + .warnDeprecated(warnDeprecated) + .enableCompatibility(compatibilityFilter != null); } - /** - * Configuration of a {@link RuleSetFactory}. This is a fluent builder - * pattern. Use this instead of the constructors of RuleSetFactory. - */ - public static final class RuleSetFactoryConfig { - - ResourceLoader resourceLoader = new ResourceLoader(RuleSetFactoryConfig.class.getClassLoader()); - RulePriority minimumPriority = RulePriority.LOW; - boolean warnDeprecated = true; - boolean enableCompatibility = true; - boolean includeDeprecatedRuleReferences = false; - - /** - * Specify that the given classloader should be used to resolve - * paths to external ruleset references. The default uses PMD's - * own classpath. - */ - public RuleSetFactoryConfig loadResourcesWith(ClassLoader classLoader) { - this.resourceLoader = new ResourceLoader(classLoader); - return this; - } - - // internal - RuleSetFactoryConfig loadResourcesWith(ResourceLoader loader) { - this.resourceLoader = loader; - return this; - } - - /** - * Filter loaded rules to only those that match or are above - * the given priority. The default is {@link RulePriority#LOW}, - * ie, no filtering occurs. - */ - public RuleSetFactoryConfig filterAbovePriority(RulePriority minimumPriority) { - this.minimumPriority = minimumPriority; - return this; - } - - /** - * Log a warning when referencing a deprecated rule. - * This is enabled by default. - */ - public RuleSetFactoryConfig warnDeprecated(boolean warn) { - this.warnDeprecated = warn; - return this; - } - - /** - * Enable translating old rule references to newer ones, if they have - * been moved or renamed. This is enabled by default, if disabled, - * unresolved references will not be translated and will produce an - * error. - */ - public RuleSetFactoryConfig enableCompatibility(boolean enable) { - this.enableCompatibility = enable; - return this; - } - - /** - * Follow deprecated rule references. By default this is off, - * and those references will be ignored (with a warning depending - * on {@link #enableCompatibility(boolean)}). - */ - public RuleSetFactoryConfig includeDeprecatedRuleReferences(boolean enable) { - this.includeDeprecatedRuleReferences = enable; - return this; - } - - public RuleSetFactory createFactory() { - return new RuleSetFactory(this); - } - - /** - * Configure a new ruleset factory builder according to the parameters - * of the given PMD configuration. - */ - public static RuleSetFactoryConfig fromPmdConfig(PMDConfiguration configuration) { - return new RuleSetFactoryConfig().filterAbovePriority(configuration.getMinimumPriority()) - .enableCompatibility(configuration.isRuleSetFactoryCompatibilityEnabled()); - } - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java index a0008ab3b4..26393af8ea 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java @@ -18,7 +18,6 @@ import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; -import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; import net.sourceforge.pmd.annotation.InternalApi; /** @@ -28,7 +27,7 @@ import net.sourceforge.pmd.annotation.InternalApi; * * @see issue 1360 * - * @deprecated Use {@link RuleSetFactoryConfig#enableCompatibility(boolean)} to enable this feature. + * @deprecated Use {@link RuleSetParserConfig#enableCompatibility(boolean)} to enable this feature. * This implementation is internal API. */ @InternalApi diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParserConfig.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParserConfig.java new file mode 100644 index 0000000000..c1b1f6fe15 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParserConfig.java @@ -0,0 +1,96 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd; + +import net.sourceforge.pmd.util.ResourceLoader; + +/** + * Configuration of a {@link RuleSetFactory}. This is a fluent builder + * pattern. Use this instead of the constructors of RuleSetFactory. + */ +public final class RuleSetParserConfig { + + ResourceLoader resourceLoader = new ResourceLoader(RuleSetParserConfig.class.getClassLoader()); + RulePriority minimumPriority = RulePriority.LOW; + boolean warnDeprecated = true; + boolean enableCompatibility = true; + boolean includeDeprecatedRuleReferences = false; + + /** + * Create a new config with the default values. + */ + public RuleSetParserConfig() { + + } + + /** + * Specify that the given classloader should be used to resolve + * paths to external ruleset references. The default uses PMD's + * own classpath. + */ + public RuleSetParserConfig loadResourcesWith(ClassLoader classLoader) { + this.resourceLoader = new ResourceLoader(classLoader); + return this; + } + + // internal + RuleSetParserConfig loadResourcesWith(ResourceLoader loader) { + this.resourceLoader = loader; + return this; + } + + /** + * Filter loaded rules to only those that match or are above + * the given priority. The default is {@link RulePriority#LOW}, + * ie, no filtering occurs. + */ + public RuleSetParserConfig filterAbovePriority(RulePriority minimumPriority) { + this.minimumPriority = minimumPriority; + return this; + } + + /** + * Log a warning when referencing a deprecated rule. + * This is enabled by default. + */ + public RuleSetParserConfig warnDeprecated(boolean warn) { + this.warnDeprecated = warn; + return this; + } + + /** + * Enable translating old rule references to newer ones, if they have + * been moved or renamed. This is enabled by default, if disabled, + * unresolved references will not be translated and will produce an + * error. + */ + public RuleSetParserConfig enableCompatibility(boolean enable) { + this.enableCompatibility = enable; + return this; + } + + /** + * Follow deprecated rule references. By default this is off, + * and those references will be ignored (with a warning depending + * on {@link #enableCompatibility(boolean)}). + */ + public RuleSetParserConfig includeDeprecatedRuleReferences(boolean enable) { + this.includeDeprecatedRuleReferences = enable; + return this; + } + + public RuleSetFactory createFactory() { + return new RuleSetFactory(this); + } + + /** + * Configure a new ruleset factory builder according to the parameters + * of the given PMD configuration. + */ + public static RuleSetParserConfig fromPmdConfig(PMDConfiguration configuration) { + return new RuleSetParserConfig().filterAbovePriority(configuration.getMinimumPriority()) + .enableCompatibility(configuration.isRuleSetFactoryCompatibilityEnabled()); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java index 609c7b6549..bba3ef5f55 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd; import java.util.logging.Level; import java.util.logging.Logger; -import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; @@ -77,7 +76,7 @@ public final class RulesetsFactoryUtils { } /** - * @deprecated Use a {@link RuleSetFactoryConfig} + * @deprecated Use a {@link RuleSetParserConfig} */ @InternalApi @Deprecated @@ -98,7 +97,7 @@ public final class RulesetsFactoryUtils { * * @see #createFactory(PMDConfiguration, ClassLoader) * - * @deprecated Use {@link RuleSetFactoryConfig#fromPmdConfig(PMDConfiguration)} + * @deprecated Use {@link RuleSetParserConfig#fromPmdConfig(PMDConfiguration)} */ @Deprecated public static RuleSetFactory createFactory(final PMDConfiguration configuration) { @@ -111,7 +110,7 @@ public final class RulesetsFactoryUtils { * * @return A ruleset factory * - * @see RuleSetFactoryConfig + * @see RuleSetParserConfig */ public static RuleSetFactory defaultFactory() { return new RuleSetFactory(); @@ -129,7 +128,7 @@ public final class RulesetsFactoryUtils { * * @see #createFactory(PMDConfiguration) * - * @deprecated Use a {@link RuleSetFactoryConfig} + * @deprecated Use a {@link RuleSetParserConfig} */ @Deprecated public static RuleSetFactory createFactory(final PMDConfiguration configuration, ClassLoader classLoader) { @@ -153,7 +152,7 @@ public final class RulesetsFactoryUtils { * * @see #createFactory(PMDConfiguration) * - * @deprecated Use a {@link RuleSetFactoryConfig} + * @deprecated Use a {@link RuleSetParserConfig} */ @Deprecated public static RuleSetFactory createFactory(ClassLoader classLoader, @@ -177,7 +176,7 @@ public final class RulesetsFactoryUtils { * * @see #createFactory(PMDConfiguration) * - * @deprecated Use a {@link RuleSetFactoryConfig} + * @deprecated Use a {@link RuleSetParserConfig} */ @Deprecated public static RuleSetFactory createFactory(RulePriority minimumPriority, 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 0abe673de8..6a8b4750de 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 @@ -29,7 +29,7 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; +import net.sourceforge.pmd.RuleSetParserConfig; import net.sourceforge.pmd.RuleSetNotFoundException; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.ant.Formatter; @@ -102,9 +102,9 @@ public class PMDTaskImpl { setupClassLoader(); // Setup RuleSetFactory and validate RuleSets - RuleSetFactory ruleSetFactory = RuleSetFactoryConfig.fromPmdConfig(configuration) - .loadResourcesWith(setupResourceLoader()) - .createFactory(); + RuleSetFactory ruleSetFactory = RuleSetParserConfig.fromPmdConfig(configuration) + .loadResourcesWith(setupResourceLoader()) + .createFactory(); try { // This is just used to validate and display rules. Each thread will create its own ruleset diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 840def81e1..75de7f2189 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -23,7 +23,6 @@ import org.apache.commons.lang3.StringUtils; import org.junit.Test; import org.junit.rules.ExpectedException; -import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; import net.sourceforge.pmd.junit.JavaUtilLoggingRule; import net.sourceforge.pmd.junit.LocaleRule; import net.sourceforge.pmd.lang.DummyLanguageModule; @@ -506,7 +505,7 @@ public class RuleSetFactoryTest { @Test public void testReferencePriority() throws RuleSetNotFoundException { - RuleSetFactoryConfig config = new RuleSetFactoryConfig().warnDeprecated(false).enableCompatibility(true); + RuleSetParserConfig config = new RuleSetParserConfig().warnDeprecated(false).enableCompatibility(true); RuleSetFactory rsf = config.filterAbovePriority(RulePriority.LOW).createFactory(); RuleSet ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); @@ -547,7 +546,7 @@ public class RuleSetFactoryTest { @Test public void testOverridePriorityLoadWithMinimum() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetFactoryConfig().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true).createFactory(); + RuleSetFactory rsf = new RuleSetParserConfig().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true).createFactory(); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); // only one rule should remain, since we filter out the other rule by minimum priority assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -568,13 +567,13 @@ public class RuleSetFactoryTest { @Test public void testExcludeWithMinimumPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetFactoryConfig().filterAbovePriority(RulePriority.HIGH).createFactory(); + RuleSetFactory rsf = new RuleSetParserConfig().filterAbovePriority(RulePriority.HIGH).createFactory(); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // no rules should be loaded assertEquals("Number of Rules", 0, ruleset.getRules().size()); // now, load with default minimum priority - rsf = new RuleSetFactoryConfig().filterAbovePriority(RulePriority.LOW).createFactory(); + rsf = new RuleSetParserConfig().filterAbovePriority(RulePriority.LOW).createFactory(); ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // only one rule, we have excluded one... assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -603,9 +602,9 @@ public class RuleSetFactoryTest { @Test public void testSetPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetFactoryConfig().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false).createFactory(); + RuleSetFactory rsf = new RuleSetParserConfig().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false).createFactory(); assertEquals(0, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); - rsf = new RuleSetFactoryConfig().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false).createFactory(); + rsf = new RuleSetParserConfig().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false).createFactory(); assertEquals(1, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); } @@ -780,7 +779,7 @@ public class RuleSetFactoryTest { + " Ruleset which references a empty ruleset\n" + "\n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetFactoryConfig().loadResourcesWith(new ResourceLoader()).filterAbovePriority(RulePriority.LOW).warnDeprecated(true).enableCompatibility(true).createFactory(); + RuleSetFactory ruleSetFactory = new RuleSetParserConfig().loadResourcesWith(new ResourceLoader()).filterAbovePriority(RulePriority.LOW).warnDeprecated(true).enableCompatibility(true).createFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet(ref); assertEquals(0, ruleset.getRules().size()); @@ -1285,7 +1284,7 @@ public class RuleSetFactoryTest { } private RuleSet loadRuleSetWithDeprecationWarnings(String ruleSetXml) throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetFactoryConfig().warnDeprecated(true).enableCompatibility(false).createFactory(); + RuleSetFactory rsf = new RuleSetParserConfig().warnDeprecated(true).enableCompatibility(false).createFactory(); return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml)); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java index 135294a762..7954efdc88 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java @@ -9,13 +9,13 @@ import org.junit.Test; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; +import net.sourceforge.pmd.RuleSetParserConfig; public class PMD5RulesetTest { @Test public void loadRuleset() throws Exception { - RuleSetFactory ruleSetFactory = new RuleSetFactoryConfig().createFactory(); + RuleSetFactory ruleSetFactory = new RuleSetParserConfig().createFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet("net/sourceforge/pmd/lang/java/pmd5ruleset.xml"); Assert.assertNotNull(ruleset); Assert.assertNull(ruleset.getRuleByName("GuardLogStatementJavaUtil")); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java index fa3bb3f027..2daa2c1c39 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java @@ -16,7 +16,7 @@ import org.junit.contrib.java.lang.system.SystemErrRule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetFactory.RuleSetFactoryConfig; +import net.sourceforge.pmd.RuleSetParserConfig; import net.sourceforge.pmd.RuleSetNotFoundException; public class QuickstartRulesetTest { @@ -49,7 +49,7 @@ public class QuickstartRulesetTest { } }); - RuleSetFactory ruleSetFactory = new RuleSetFactoryConfig().enableCompatibility(false).createFactory(); + RuleSetFactory ruleSetFactory = new RuleSetParserConfig().enableCompatibility(false).createFactory(); RuleSet quickstart = ruleSetFactory.createRuleSet("rulesets/java/quickstart.xml"); Assert.assertFalse(quickstart.getRules().isEmpty()); } 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 1f37f484c7..6c7ac28b46 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 @@ -13,8 +13,8 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSets; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; import net.sourceforge.pmd.lang.scala.ScalaLanguageModule; @@ -38,7 +38,7 @@ public final class ScalaParsingHelper extends BaseParsingHelper Date: Mon, 26 Oct 2020 19:31:48 +0100 Subject: [PATCH 06/98] Isolate single rule pattern --- .../java/net/sourceforge/pmd/RuleSet.java | 24 +++++++++++++++++++ .../net/sourceforge/pmd/RuleSetFactory.java | 23 ------------------ .../pmd/benchmark/Benchmarker.java | 2 +- .../pmd/util/designer/Designer.java | 5 ++-- .../java/net/sourceforge/pmd/RuleSetTest.java | 10 ++++---- .../sourceforge/pmd/RuleSetWriterTest.java | 2 +- .../properties/PropertyDescriptorTest.java | 5 ++-- .../net/sourceforge/pmd/ExcludeLinesTest.java | 2 +- .../xpath/XPathMetricFunctionTest.java | 3 +-- .../pmd/lang/java/rule/XPathRuleTest.java | 3 +-- .../pmd/lang/jsp/ast/XPathJspRuleTest.java | 3 +-- .../lang/scala/ast/ScalaParsingHelper.java | 3 +-- .../pmd/testframework/RuleTst.java | 2 +- 13 files changed, 41 insertions(+), 46 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java index c22906166a..3c88301211 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java @@ -28,6 +28,7 @@ import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.RuleReference; +import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.util.filter.Filter; import net.sourceforge.pmd.util.filter.Filters; @@ -100,6 +101,29 @@ public class RuleSet implements ChecksumAware { filter = rs.filter; // filters are immutable, can be shared } + /** + * Creates a new RuleSet containing a single rule. + * + * @param rule The rule being created + * + * @return The newly created RuleSet + */ + public static RuleSet forSingleRule(final Rule rule) { + final long checksum; + if (rule instanceof XPathRule) { + checksum = ((XPathRule) rule).getXPathExpression().hashCode(); + } else { + // TODO : Is this good enough? all properties' values + rule name + checksum = rule.getPropertiesByPropertyDescriptor().values().hashCode() * 31 + rule.getName().hashCode(); + } + + final RuleSetBuilder builder = new RuleSetBuilder(checksum) + .withName(rule.getName()) + .withDescription("RuleSet for " + rule.getName()); + builder.addRule(rule); + return builder.build(); + } + /* package */ static class RuleSetBuilder { public String description; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 6163e96c89..da00e5c7f7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -308,29 +308,6 @@ public class RuleSetFactory { return result; } - /** - * Creates a new RuleSet containing a single rule. - * - * @param rule - * The rule being created - * @return The newly created RuleSet - */ - public RuleSet createSingleRuleRuleSet(final Rule rule) { // TODO make static? - final long checksum; - if (rule instanceof XPathRule) { - checksum = ((XPathRule) rule).getXPathExpression().hashCode(); - } else { - // TODO : Is this good enough? all properties' values + rule name - checksum = rule.getPropertiesByPropertyDescriptor().values().hashCode() * 31 + rule.getName().hashCode(); - } - - final RuleSetBuilder builder = new RuleSetBuilder(checksum) - .withName(rule.getName()) - .withDescription("RuleSet for " + rule.getName()); - builder.addRule(rule); - return builder.build(); - } - /** * Create a Rule from a RuleSet created from a file name resource. The * currently configured ResourceLoader is used. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java b/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java index 42baa667b6..89a81b28e0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java @@ -181,7 +181,7 @@ public final class Benchmarker { System.out.println("Starting " + rule.getName()); } - final RuleSet working = factory.createSingleRuleRuleSet(rule); + final RuleSet working = RuleSet.forSingleRule(rule); RuleSets ruleSets = new RuleSets(working); PMDConfiguration config = new PMDConfiguration(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/designer/Designer.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/designer/Designer.java index 1ece0350ac..588ff6f7ff 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/designer/Designer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/designer/Designer.java @@ -103,7 +103,6 @@ import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.PMDVersion; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.SourceCodeProcessor; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -564,7 +563,7 @@ public class Designer implements ClipboardOwner { LanguageVersion languageVersion = getLanguageVersion(); DFAGraphRule dfaGraphRule = languageVersion.getLanguageVersionHandler().getDFAGraphRule(); if (dfaGraphRule != null) { - final RuleSet rs = new RuleSetFactory().createSingleRuleRuleSet(dfaGraphRule); + final RuleSet rs = RuleSet.forSingleRule(dfaGraphRule); RuleContext ctx = new RuleContext(); ctx.setSourceCodeFile(new File("[no filename]." + languageVersion.getLanguage().getExtensions().get(0))); PMDConfiguration config = new PMDConfiguration(); @@ -610,7 +609,7 @@ public class Designer implements ClipboardOwner { xpathRule.setXPath(xpathQueryArea.getText()); xpathRule.setVersion(xpathVersionButtonGroup.getSelection().getActionCommand()); - final RuleSet ruleSet = new RuleSetFactory().createSingleRuleRuleSet(xpathRule); + final RuleSet ruleSet = RuleSet.forSingleRule(xpathRule); RuleSets ruleSets = new RuleSets(ruleSet); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java index 7a275bc011..7c0f5c8813 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java @@ -60,7 +60,7 @@ public class RuleSetTest { public void testNoDFA() { MockRule mock = new MockRule("name", "desc", "msg", "rulesetname"); mock.setLanguage(LanguageRegistry.getLanguage(DummyLanguageModule.NAME)); - RuleSet rs = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(mock); + RuleSet rs = RuleSet.forSingleRule(mock); assertFalse(rs.usesDFA(LanguageRegistry.getLanguage(DummyLanguageModule.NAME))); } @@ -69,7 +69,7 @@ public class RuleSetTest { MockRule mock = new MockRule("name", "desc", "msg", "rulesetname"); mock.setLanguage(LanguageRegistry.getLanguage(DummyLanguageModule.NAME)); mock.setDfa(true); - RuleSet rs = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(mock); + RuleSet rs = RuleSet.forSingleRule(mock); assertTrue(rs.usesDFA(LanguageRegistry.getLanguage(DummyLanguageModule.NAME))); } @@ -88,21 +88,21 @@ public class RuleSetTest { @Test public void testGetRuleByName() { MockRule mock = new MockRule("name", "desc", "msg", "rulesetname"); - RuleSet rs = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(mock); + RuleSet rs = RuleSet.forSingleRule(mock); assertEquals("unable to fetch rule by name", mock, rs.getRuleByName("name")); } @Test public void testGetRuleByName2() { MockRule mock = new MockRule("name", "desc", "msg", "rulesetname"); - RuleSet rs = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(mock); + RuleSet rs = RuleSet.forSingleRule(mock); assertNull("the rule FooRule must not be found!", rs.getRuleByName("FooRule")); } @Test public void testRuleList() { MockRule rule = new MockRule("name", "desc", "msg", "rulesetname"); - RuleSet ruleset = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); + RuleSet ruleset = RuleSet.forSingleRule(rule); assertEquals("Size of RuleSet isn't one.", 1, ruleset.size()); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java index d488f30fcc..6f6e1643dc 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java @@ -81,7 +81,7 @@ public class RuleSetWriterTest { ruleRef.setRuleSetReference(ruleSetReference); ruleRef.setName("Foo"); // override the name - RuleSet ruleSet = ruleSetFactory.createSingleRuleRuleSet(ruleRef); + RuleSet ruleSet = RuleSet.forSingleRule(ruleRef); writer.write(ruleSet); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java index bba31ae3dc..7c82a7fa3e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/properties/PropertyDescriptorTest.java @@ -30,7 +30,6 @@ import org.junit.rules.ExpectedException; import net.sourceforge.pmd.FooRule; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.properties.constraints.PropertyConstraint; @@ -57,7 +56,7 @@ public class PropertyDescriptorTest { FooRule rule = new FooRule(); rule.definePropertyDescriptor(intProperty); rule.setProperty(intProperty, 1000); - RuleSet ruleSet = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); + RuleSet ruleSet = RuleSet.forSingleRule(rule); List dysfunctional = new ArrayList<>(); ruleSet.removeDysfunctionalRules(dysfunctional); @@ -78,7 +77,7 @@ public class PropertyDescriptorTest { FooRule rule = new FooRule(); rule.definePropertyDescriptor(descriptor); rule.setProperty(descriptor, Collections.singletonList(1000d)); // not in range - RuleSet ruleSet = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); + RuleSet ruleSet = RuleSet.forSingleRule(rule); List dysfunctional = new ArrayList<>(); ruleSet.removeDysfunctionalRules(dysfunctional); 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 5704ef0951..2d68f04ba7 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java @@ -41,7 +41,7 @@ public class ExcludeLinesTest extends RuleTst { ctx.setReport(r); ctx.setSourceCodeFile(new File("n/a")); ctx.setLanguageVersion(LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getDefaultVersion()); - RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); + RuleSet rules = RuleSet.forSingleRule(rule); p.getSourceCodeProcessor().processSourceCode(new StringReader(TEST3), new RuleSets(rules), ctx); assertTrue(r.isEmpty()); assertEquals(r.getSuppressedRuleViolations().size(), 1); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java index 2dc75f74cc..1114a434ee 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/xpath/XPathMetricFunctionTest.java @@ -21,7 +21,6 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.xpath.MetricFunction; @@ -57,7 +56,7 @@ public class XPathMetricFunctionTest { ctx.setReport(report); ctx.setSourceCodeFile(new File("n/a")); ctx.setIgnoreExceptions(false); // for test, we want immediate exceptions thrown and not collect them - RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); + RuleSet rules = RuleSet.forSingleRule(rule); p.getSourceCodeProcessor().processSourceCode(new StringReader(code), new RuleSets(rules), ctx); return report.iterator(); } 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 90e01fabe5..e563974933 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 @@ -22,7 +22,6 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.Parser; @@ -206,7 +205,7 @@ public class XPathRuleTest extends RuleTst { Report report = new Report(); ctx.setReport(report); ctx.setSourceCodeFile(new File("n/a")); - RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(r); + RuleSet rules = RuleSet.forSingleRule(r); p.getSourceCodeProcessor().processSourceCode(new StringReader(test), new RuleSets(rules), ctx); return report; } diff --git a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java index 3597fa8496..9f551f3a11 100644 --- a/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java +++ b/pmd-jsp/src/test/java/net/sourceforge/pmd/lang/jsp/ast/XPathJspRuleTest.java @@ -18,7 +18,6 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.jsp.JspLanguageModule; import net.sourceforge.pmd.lang.rule.XPathRule; @@ -35,7 +34,7 @@ public class XPathJspRuleTest extends RuleTst { Rule rule = new XPathRule(XPATH_EXPRESSION); rule.setMessage("Test"); rule.setLanguage(LanguageRegistry.getLanguage(JspLanguageModule.NAME)); - RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); + RuleSet rules = RuleSet.forSingleRule(rule); RuleContext ctx = new RuleContext(); Report report = new Report(); 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 6c7ac28b46..984277dd0a 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 @@ -14,7 +14,6 @@ import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSets; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; import net.sourceforge.pmd.lang.scala.ScalaLanguageModule; @@ -38,7 +37,7 @@ public final class ScalaParsingHelper extends BaseParsingHelper Date: Mon, 26 Oct 2020 19:47:42 +0100 Subject: [PATCH 07/98] Deprecate other APIs --- .../pmd/lang/apex/DefaultRulesetTest.java | 4 +- .../main/java/net/sourceforge/pmd/PMD.java | 2 +- .../java/net/sourceforge/pmd/RuleSet.java | 64 ++++++++- .../net/sourceforge/pmd/RuleSetFactory.java | 74 +++++++--- .../pmd/RuleSetFactoryCompatibility.java | 2 +- .../net/sourceforge/pmd/RuleSetParser.java | 131 ++++++++++++++++++ .../sourceforge/pmd/RuleSetParserConfig.java | 96 ------------- .../sourceforge/pmd/RuleSetReferenceId.java | 5 + .../java/net/sourceforge/pmd/RuleSets.java | 18 ++- .../sourceforge/pmd/RulesetsFactoryUtils.java | 20 ++- .../pmd/ant/internal/PMDTaskImpl.java | 14 +- .../sourceforge/pmd/RuleSetFactoryTest.java | 16 +-- .../pmd/lang/java/PMD5RulesetTest.java | 4 +- .../pmd/lang/java/QuickstartRulesetTest.java | 4 +- 14 files changed, 298 insertions(+), 156 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParserConfig.java diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java index c2d76f7d1f..ca06fce388 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java @@ -16,13 +16,13 @@ import org.junit.contrib.java.lang.system.SystemErrRule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetParserConfig; +import net.sourceforge.pmd.RuleSetParser; public class DefaultRulesetTest { @Rule public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests(); - private RuleSetFactory factory = new RuleSetParserConfig().enableCompatibility(false).createFactory(); + private RuleSetFactory factory = new RuleSetParser().enableCompatibility(false).createFactory(); @Test public void loadDefaultRuleset() throws Exception { 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 c7aaf5877e..cd956bb64c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -202,7 +202,7 @@ public class PMD { public static int doPMD(PMDConfiguration configuration) { // Load the RuleSets - final RuleSetFactory ruleSetFactory = RuleSetParserConfig.fromPmdConfig(configuration).createFactory(); + final RuleSetFactory ruleSetFactory = RuleSetParser.fromPmdConfig(configuration).createFactory(); final RuleSets ruleSets = RulesetsFactoryUtils.getRuleSetsWithBenchmark(configuration.getRuleSets(), ruleSetFactory); if (ruleSets == null) { return PMDCommandLineInterface.NO_ERRORS_STATUS; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java index 3c88301211..f53033e5dc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java @@ -102,7 +102,8 @@ public class RuleSet implements ChecksumAware { } /** - * Creates a new RuleSet containing a single rule. + * Creates a new ruleset containing a single rule. The ruleset will + * have default description, name, and null file name. * * @param rule The rule being created * @@ -117,13 +118,72 @@ public class RuleSet implements ChecksumAware { checksum = rule.getPropertiesByPropertyDescriptor().values().hashCode() * 31 + rule.getName().hashCode(); } - final RuleSetBuilder builder = new RuleSetBuilder(checksum) + final RuleSetBuilder builder = + new RuleSetBuilder(checksum) .withName(rule.getName()) .withDescription("RuleSet for " + rule.getName()); builder.addRule(rule); return builder.build(); } + + /** + * Creates a new ruleset with the given metadata such as name, description, + * fileName, exclude/include patterns are used. The rules are taken from the given + * collection. + * + *

Note: The rule instances are shared between the collection + * and the new ruleset (copy-by-reference). This might lead to concurrency issues, + * if the rules of the collection are also referenced by other rulesets and used + * in different threads. + *

+ * + * @param name the name of the ruleset + * @param description the description + * @param fileName the filename + * @param excludePatterns list of exclude patterns + * @param includePatterns list of include patterns, that override the exclude patterns + * @param rules the collection with the rules to add to the new ruleset + * + * @return the new ruleset + * + * @throws NullPointerException If any parameter is null, or the collections contain null elements + */ + public static RuleSet create(String name, + String description, + String fileName, + Collection excludePatterns, + Collection includePatterns, + Iterable rules) { + RuleSetBuilder builder = new RuleSetBuilder(0L); // TODO: checksum missing + builder.withName(name) + .withDescription(description) + .withFileName(fileName) + .replaceFileExclusions(excludePatterns) + .replaceFileInclusions(includePatterns); + for (Rule rule : rules) { + builder.addRule(rule); + } + return builder.build(); + } + + /** + * Creates a copy of the given ruleset. All properties like name, description, fileName + * and exclude/include patterns are copied. + * + *

Note: The rule instances are shared between the original + * and the new ruleset (copy-by-reference). This might lead to concurrency issues, + * if the original ruleset and the new ruleset are used in different threads. + *

+ * + * @param original the original rule set to copy from + * + * @return the copy + */ + public static RuleSet copy(RuleSet original) { + return new RuleSet(original); + } + /* package */ static class RuleSetBuilder { public String description; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index da00e5c7f7..8200c6d525 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -38,16 +38,18 @@ import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.MockRule; import net.sourceforge.pmd.lang.rule.RuleReference; -import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.rules.RuleFactory; import net.sourceforge.pmd.util.ResourceLoader; /** * RuleSetFactory is responsible for creating RuleSet instances from XML - * content. See {@link RuleSetParserConfig} for configuration options and - * their defaults. The constructors of this class are deprecated and their - * usages should be replaced by use of {@link RuleSetParserConfig#createFactory()}. + * content. See {@link RuleSetParser} for configuration options and + * their defaults. + * + * @deprecated Use a {@link RuleSetParser} instead. This will be hidden in PMD 7 + * (it's the implementation, while {@link RuleSetParser} is the API). */ +@Deprecated public class RuleSetFactory { private static final Logger LOG = Logger.getLogger(RuleSetFactory.class.getName()); @@ -65,7 +67,7 @@ public class RuleSetFactory { private final Map parsedRulesets = new HashMap<>(); /** - * @deprecated Use a {@link RuleSetParserConfig} to build a new factory + * @deprecated Use a {@link RuleSetParser} to build a new factory */ @Deprecated // to be removed with PMD 7.0.0. public RuleSetFactory() { @@ -73,7 +75,7 @@ public class RuleSetFactory { } /** - * @deprecated Use a {@link RuleSetParserConfig} to build a new factory + * @deprecated Use a {@link RuleSetParser} to build a new factory */ @Deprecated // to be removed with PMD 7.0.0. public RuleSetFactory(final ClassLoader classLoader, final RulePriority minimumPriority, @@ -82,7 +84,7 @@ public class RuleSetFactory { } /** - * @deprecated Use a {@link RuleSetParserConfig} to build a new factory + * @deprecated Use a {@link RuleSetParser} to build a new factory */ @Deprecated // to be hidden with PMD 7.0.0. public RuleSetFactory(final ResourceLoader resourceLoader, final RulePriority minimumPriority, @@ -121,7 +123,7 @@ public class RuleSetFactory { } - RuleSetFactory(RuleSetParserConfig config) { + RuleSetFactory(RuleSetParser config) { this(config.resourceLoader, config.minimumPriority, config.warnDeprecated, config.enableCompatibility, config.includeDeprecatedRuleReferences); } @@ -142,7 +144,10 @@ public class RuleSetFactory { * @return An Iterator of RuleSet objects. * * @throws RuleSetNotFoundException if the ruleset file could not be found + * + * @deprecated This is apparently only used in code deprecated for removal */ + @Deprecated public Iterator getRegisteredRuleSets() throws RuleSetNotFoundException { String rulesetsProperties = null; List ruleSetReferenceIds = new ArrayList<>(); @@ -177,7 +182,10 @@ public class RuleSetFactory { * @return The new RuleSets. * @throws RuleSetNotFoundException * if unable to find a resource. + * + * @deprecated Use {@link RuleSetParser#parseFromResourceReference(String)} */ + @Deprecated public RuleSets createRuleSets(String referenceString) throws RuleSetNotFoundException { return createRuleSets(RuleSetReferenceId.parse(referenceString)); } @@ -191,7 +199,10 @@ public class RuleSetFactory { * @return The new RuleSets. * @throws RuleSetNotFoundException * if unable to find a resource. + * + * @deprecated Will not be replaced */ + @Deprecated public RuleSets createRuleSets(List ruleSetReferenceIds) throws RuleSetNotFoundException { RuleSets ruleSets = new RuleSets(); for (RuleSetReferenceId ruleSetReferenceId : ruleSetReferenceIds) { @@ -213,7 +224,10 @@ public class RuleSetFactory { * @return A new RuleSet. * @throws RuleSetNotFoundException * if unable to find a resource. + * + * @deprecated Use {@link RuleSetParser#parseFromResourceReference(String)} and discard the rest of the list. */ + @Deprecated public RuleSet createRuleSet(String referenceString) throws RuleSetNotFoundException { List references = RuleSetReferenceId.parse(referenceString); if (references.isEmpty()) { @@ -232,7 +246,10 @@ public class RuleSetFactory { * @return A new RuleSet. * @throws RuleSetNotFoundException * if unable to find a resource. + * + * @deprecated Will not be replaced */ + @Deprecated public RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { return createRuleSet(ruleSetReferenceId, includeDeprecatedRuleReferences); } @@ -253,7 +270,10 @@ public class RuleSetFactory { * * @param original the original rule set to copy from * @return the copy + * + * @deprecated Use {@link RuleSet#copy(RuleSet)} */ + @Deprecated public RuleSet createRuleSetCopy(RuleSet original) { RuleSetBuilder builder = new RuleSetBuilder(original); return builder.build(); @@ -277,23 +297,17 @@ public class RuleSetFactory { * @param includePatterns list of include patterns, if any is not a valid regular expression, it will be ignored * @param rules the collection with the rules to add to the new ruleset * @return the new ruleset + * + * @deprecated Use {@link RuleSet#create(String, String, String, Collection, Collection, Iterable)} */ + @Deprecated public RuleSet createNewRuleSet(String name, String description, String fileName, Collection excludePatterns, Collection includePatterns, Collection rules) { - RuleSetBuilder builder = new RuleSetBuilder(0L); // TODO: checksum missing - builder.withName(name) - .withDescription(description) - .withFileName(fileName) - .replaceFileExclusions(toPatterns(excludePatterns)) - .replaceFileInclusions(toPatterns(includePatterns)); - for (Rule rule : rules) { - builder.addRule(rule); - } - return builder.build(); + return RuleSet.create(name, description, fileName, toPatterns(excludePatterns), toPatterns(includePatterns), rules); } private Collection toPatterns(Collection sources) { @@ -308,6 +322,20 @@ public class RuleSetFactory { return result; } + /** + * Creates a new RuleSet containing a single rule. + * + * @param rule The rule being created + * + * @return The newly created RuleSet + * + * @deprecated Use {@link RuleSet#forSingleRule(Rule)} + */ + @Deprecated + public RuleSet createSingleRuleRuleSet(final Rule rule) { + return RuleSet.forSingleRule(rule); + } + /** * Create a Rule from a RuleSet created from a file name resource. The * currently configured ResourceLoader is used. @@ -815,11 +843,11 @@ public class RuleSetFactory { } - public RuleSetParserConfig toConfig() { - return new RuleSetParserConfig().loadResourcesWith(resourceLoader) - .filterAbovePriority(minimumPriority) - .warnDeprecated(warnDeprecated) - .enableCompatibility(compatibilityFilter != null); + public RuleSetParser toConfig() { + return new RuleSetParser().loadResourcesWith(resourceLoader) + .filterAbovePriority(minimumPriority) + .warnDeprecated(warnDeprecated) + .enableCompatibility(compatibilityFilter != null); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java index 26393af8ea..b3d5d51a7b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java @@ -27,7 +27,7 @@ import net.sourceforge.pmd.annotation.InternalApi; * * @see issue 1360 * - * @deprecated Use {@link RuleSetParserConfig#enableCompatibility(boolean)} to enable this feature. + * @deprecated Use {@link RuleSetParser#enableCompatibility(boolean)} to enable this feature. * This implementation is internal API. */ @InternalApi diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java new file mode 100644 index 0000000000..f972e541fb --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java @@ -0,0 +1,131 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd; + +import java.util.ArrayList; +import java.util.List; + +import net.sourceforge.pmd.util.ResourceLoader; + +/** + * Configurable ruleset parser. Note that this replaces the API of {@link RulesetsFactoryUtils} + * and {@link RuleSetFactory}. + */ +public final class RuleSetParser { + + ResourceLoader resourceLoader = new ResourceLoader(RuleSetParser.class.getClassLoader()); + RulePriority minimumPriority = RulePriority.LOW; + boolean warnDeprecated = true; + boolean enableCompatibility = true; + boolean includeDeprecatedRuleReferences = false; + + /** + * Create a new config with the default values. + */ + public RuleSetParser() { + + } + + /** + * Specify that the given classloader should be used to resolve + * paths to external ruleset references. The default uses PMD's + * own classpath. + */ + public RuleSetParser loadResourcesWith(ClassLoader classLoader) { + this.resourceLoader = new ResourceLoader(classLoader); + return this; + } + + // internal + RuleSetParser loadResourcesWith(ResourceLoader loader) { + this.resourceLoader = loader; + return this; + } + + /** + * Filter loaded rules to only those that match or are above + * the given priority. The default is {@link RulePriority#LOW}, + * ie, no filtering occurs. + */ + public RuleSetParser filterAbovePriority(RulePriority minimumPriority) { + this.minimumPriority = minimumPriority; + return this; + } + + /** + * Log a warning when referencing a deprecated rule. + * This is enabled by default. + */ + public RuleSetParser warnDeprecated(boolean warn) { + this.warnDeprecated = warn; + return this; + } + + /** + * Enable translating old rule references to newer ones, if they have + * been moved or renamed. This is enabled by default, if disabled, + * unresolved references will not be translated and will produce an + * error. + */ + public RuleSetParser enableCompatibility(boolean enable) { + this.enableCompatibility = enable; + return this; + } + + /** + * Follow deprecated rule references. By default this is off, + * and those references will be ignored (with a warning depending + * on {@link #enableCompatibility(boolean)}). + */ + public RuleSetParser includeDeprecatedRuleReferences(boolean enable) { + this.includeDeprecatedRuleReferences = enable; + return this; + } + + public RuleSetFactory createFactory() { + return new RuleSetFactory(this); + } + + + /** + * Create a RuleSets from a comma separated list of RuleSet reference IDs. + * This is a convenience method which calls + * {@link RuleSetReferenceId#parse(String)}, and then calls + * {@link #createRuleSets(List)}. The currently configured ResourceLoader is + * used. + * + * @param rulesetResourcePaths A comma separated list of RuleSet reference IDs. + * + * @return The new RuleSets. + */ + public List parseFromResourceReference(String rulesetResourcePaths) throws RuleSetNotFoundException { + return createRuleSets(RuleSetReferenceId.parse(rulesetResourcePaths)); + } + + private List createRuleSets(List ruleSetReferenceIds) throws RuleSetNotFoundException { + List ruleSets = new ArrayList<>(); + for (RuleSetReferenceId ruleSetReferenceId : ruleSetReferenceIds) { + RuleSet ruleSet = createRuleSet(ruleSetReferenceId); + ruleSets.add(ruleSet); + } + return ruleSets; + } + + + // package private + RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { + return new RuleSetFactory(this).createRuleSet(ruleSetReferenceId); + } + + + /** + * Configure a new ruleset factory builder according to the parameters + * of the given PMD configuration. + */ + public static RuleSetParser fromPmdConfig(PMDConfiguration configuration) { + return new RuleSetParser().filterAbovePriority(configuration.getMinimumPriority()) + .enableCompatibility(configuration.isRuleSetFactoryCompatibilityEnabled()); + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParserConfig.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParserConfig.java deleted file mode 100644 index c1b1f6fe15..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParserConfig.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd; - -import net.sourceforge.pmd.util.ResourceLoader; - -/** - * Configuration of a {@link RuleSetFactory}. This is a fluent builder - * pattern. Use this instead of the constructors of RuleSetFactory. - */ -public final class RuleSetParserConfig { - - ResourceLoader resourceLoader = new ResourceLoader(RuleSetParserConfig.class.getClassLoader()); - RulePriority minimumPriority = RulePriority.LOW; - boolean warnDeprecated = true; - boolean enableCompatibility = true; - boolean includeDeprecatedRuleReferences = false; - - /** - * Create a new config with the default values. - */ - public RuleSetParserConfig() { - - } - - /** - * Specify that the given classloader should be used to resolve - * paths to external ruleset references. The default uses PMD's - * own classpath. - */ - public RuleSetParserConfig loadResourcesWith(ClassLoader classLoader) { - this.resourceLoader = new ResourceLoader(classLoader); - return this; - } - - // internal - RuleSetParserConfig loadResourcesWith(ResourceLoader loader) { - this.resourceLoader = loader; - return this; - } - - /** - * Filter loaded rules to only those that match or are above - * the given priority. The default is {@link RulePriority#LOW}, - * ie, no filtering occurs. - */ - public RuleSetParserConfig filterAbovePriority(RulePriority minimumPriority) { - this.minimumPriority = minimumPriority; - return this; - } - - /** - * Log a warning when referencing a deprecated rule. - * This is enabled by default. - */ - public RuleSetParserConfig warnDeprecated(boolean warn) { - this.warnDeprecated = warn; - return this; - } - - /** - * Enable translating old rule references to newer ones, if they have - * been moved or renamed. This is enabled by default, if disabled, - * unresolved references will not be translated and will produce an - * error. - */ - public RuleSetParserConfig enableCompatibility(boolean enable) { - this.enableCompatibility = enable; - return this; - } - - /** - * Follow deprecated rule references. By default this is off, - * and those references will be ignored (with a warning depending - * on {@link #enableCompatibility(boolean)}). - */ - public RuleSetParserConfig includeDeprecatedRuleReferences(boolean enable) { - this.includeDeprecatedRuleReferences = enable; - return this; - } - - public RuleSetFactory createFactory() { - return new RuleSetFactory(this); - } - - /** - * Configure a new ruleset factory builder according to the parameters - * of the given PMD configuration. - */ - public static RuleSetParserConfig fromPmdConfig(PMDConfiguration configuration) { - return new RuleSetParserConfig().filterAbovePriority(configuration.getMinimumPriority()) - .enableCompatibility(configuration.isRuleSetFactoryCompatibilityEnabled()); - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java index e5ee6c47cc..bac49f5f86 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java @@ -14,6 +14,7 @@ import java.util.List; import org.apache.commons.lang3.StringUtils; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.util.ResourceLoader; /** @@ -78,7 +79,11 @@ import net.sourceforge.pmd.util.ResourceLoader; * * * + * + * @deprecated This is part of the internals of the {@link RuleSetParser}. */ +@Deprecated +@InternalApi public class RuleSetReferenceId { private final boolean external; private final String ruleSetFileName; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java index bbeaba74fe..c0e43ddb10 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java @@ -56,21 +56,31 @@ public class RuleSets { /** * Public constructor. Add the given rule set. * - * @param ruleSet - * the RuleSet + * @param ruleSet the RuleSet */ public RuleSets(RuleSet ruleSet) { addRuleSet(ruleSet); } + /** + * Aggregate the given rulesets. + */ + public RuleSets(List ruleSet) { + for (RuleSet set : ruleSet) { + addRuleSet(set); + } + } + /** * Add a ruleset for a language. Only one ruleset can be added for a * specific language. If ruleSet.getLanguage() is null, it is assumed to be * a RuleSet of java rules. * - * @param ruleSet - * the RuleSet + * @param ruleSet the RuleSet + * + * @deprecated Use {@link #RuleSets(List)} and don't mutate RuleSets after creation */ + @Deprecated public void addRuleSet(RuleSet ruleSet) { ruleSets.add(ruleSet); ruleChain.add(ruleSet); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java index bba3ef5f55..c835b9ac9e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java @@ -13,6 +13,10 @@ import net.sourceforge.pmd.benchmark.TimedOperation; import net.sourceforge.pmd.benchmark.TimedOperationCategory; import net.sourceforge.pmd.util.ResourceLoader; +/** + * @deprecated Use a {@link RuleSetParser} instead + */ +@Deprecated public final class RulesetsFactoryUtils { private static final Logger LOG = Logger.getLogger(RulesetsFactoryUtils.class.getName()); @@ -76,7 +80,7 @@ public final class RulesetsFactoryUtils { } /** - * @deprecated Use a {@link RuleSetParserConfig} + * @deprecated Use a {@link RuleSetParser} */ @InternalApi @Deprecated @@ -97,7 +101,7 @@ public final class RulesetsFactoryUtils { * * @see #createFactory(PMDConfiguration, ClassLoader) * - * @deprecated Use {@link RuleSetParserConfig#fromPmdConfig(PMDConfiguration)} + * @deprecated Use {@link RuleSetParser#fromPmdConfig(PMDConfiguration)} */ @Deprecated public static RuleSetFactory createFactory(final PMDConfiguration configuration) { @@ -110,7 +114,7 @@ public final class RulesetsFactoryUtils { * * @return A ruleset factory * - * @see RuleSetParserConfig + * @see RuleSetParser */ public static RuleSetFactory defaultFactory() { return new RuleSetFactory(); @@ -128,7 +132,7 @@ public final class RulesetsFactoryUtils { * * @see #createFactory(PMDConfiguration) * - * @deprecated Use a {@link RuleSetParserConfig} + * @deprecated Use a {@link RuleSetParser} */ @Deprecated public static RuleSetFactory createFactory(final PMDConfiguration configuration, ClassLoader classLoader) { @@ -152,7 +156,7 @@ public final class RulesetsFactoryUtils { * * @see #createFactory(PMDConfiguration) * - * @deprecated Use a {@link RuleSetParserConfig} + * @deprecated Use a {@link RuleSetParser} */ @Deprecated public static RuleSetFactory createFactory(ClassLoader classLoader, @@ -176,7 +180,7 @@ public final class RulesetsFactoryUtils { * * @see #createFactory(PMDConfiguration) * - * @deprecated Use a {@link RuleSetParserConfig} + * @deprecated Use a {@link RuleSetParser} */ @Deprecated public static RuleSetFactory createFactory(RulePriority minimumPriority, @@ -201,14 +205,16 @@ public final class RulesetsFactoryUtils { * @return A ruleset factory * * @see #createFactory(PMDConfiguration) + * @deprecated Use a {@link RuleSetParser} */ + @Deprecated public static RuleSetFactory createFactory(RulePriority minimumPriority, boolean warnDeprecated, boolean enableCompatibility, boolean includeDeprecatedRuleReferences) { return new RuleSetFactory(new ResourceLoader(), minimumPriority, warnDeprecated, enableCompatibility, - includeDeprecatedRuleReferences); + includeDeprecatedRuleReferences); } /** 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 6a8b4750de..f495cdfd34 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 @@ -28,9 +28,8 @@ import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetParserConfig; import net.sourceforge.pmd.RuleSetNotFoundException; +import net.sourceforge.pmd.RuleSetParser; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.ant.Formatter; import net.sourceforge.pmd.ant.PMDTask; @@ -102,9 +101,8 @@ public class PMDTaskImpl { setupClassLoader(); // Setup RuleSetFactory and validate RuleSets - RuleSetFactory ruleSetFactory = RuleSetParserConfig.fromPmdConfig(configuration) - .loadResourcesWith(setupResourceLoader()) - .createFactory(); + RuleSetParser rulesetParser = RuleSetParser.fromPmdConfig(configuration) + .loadResourcesWith(setupResourceLoader()); try { // This is just used to validate and display rules. Each thread will create its own ruleset @@ -113,8 +111,8 @@ public class PMDTaskImpl { // Substitute env variables/properties configuration.setRuleSets(project.replaceProperties(ruleSets)); } - RuleSets rules = ruleSetFactory.createRuleSets(configuration.getRuleSets()); - logRulesUsed(rules); + List rules = rulesetParser.parseFromResourceReference(configuration.getRuleSets()); + logRulesUsed(new RuleSets(rules)); } catch (RuleSetNotFoundException e) { throw new BuildException(e.getMessage(), e); } @@ -193,7 +191,7 @@ public class PMDTaskImpl { renderers.add(renderer); } try { - PMD.processFiles(configuration, ruleSetFactory, files, ctx, renderers); + PMD.processFiles(configuration, rulesetParser.createFactory(), files, ctx, renderers); } catch (RuntimeException pmde) { handleError(ctx, errorReport, pmde); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 75de7f2189..60e2c8ea63 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -505,7 +505,7 @@ public class RuleSetFactoryTest { @Test public void testReferencePriority() throws RuleSetNotFoundException { - RuleSetParserConfig config = new RuleSetParserConfig().warnDeprecated(false).enableCompatibility(true); + RuleSetParser config = new RuleSetParser().warnDeprecated(false).enableCompatibility(true); RuleSetFactory rsf = config.filterAbovePriority(RulePriority.LOW).createFactory(); RuleSet ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); @@ -546,7 +546,7 @@ public class RuleSetFactoryTest { @Test public void testOverridePriorityLoadWithMinimum() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetParserConfig().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true).createFactory(); + RuleSetFactory rsf = new RuleSetParser().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true).createFactory(); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); // only one rule should remain, since we filter out the other rule by minimum priority assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -567,13 +567,13 @@ public class RuleSetFactoryTest { @Test public void testExcludeWithMinimumPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetParserConfig().filterAbovePriority(RulePriority.HIGH).createFactory(); + RuleSetFactory rsf = new RuleSetParser().filterAbovePriority(RulePriority.HIGH).createFactory(); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // no rules should be loaded assertEquals("Number of Rules", 0, ruleset.getRules().size()); // now, load with default minimum priority - rsf = new RuleSetParserConfig().filterAbovePriority(RulePriority.LOW).createFactory(); + rsf = new RuleSetParser().filterAbovePriority(RulePriority.LOW).createFactory(); ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // only one rule, we have excluded one... assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -602,9 +602,9 @@ public class RuleSetFactoryTest { @Test public void testSetPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetParserConfig().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false).createFactory(); + RuleSetFactory rsf = new RuleSetParser().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false).createFactory(); assertEquals(0, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); - rsf = new RuleSetParserConfig().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false).createFactory(); + rsf = new RuleSetParser().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false).createFactory(); assertEquals(1, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); } @@ -779,7 +779,7 @@ public class RuleSetFactoryTest { + " Ruleset which references a empty ruleset\n" + "\n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetParserConfig().loadResourcesWith(new ResourceLoader()).filterAbovePriority(RulePriority.LOW).warnDeprecated(true).enableCompatibility(true).createFactory(); + RuleSetFactory ruleSetFactory = new RuleSetParser().loadResourcesWith(new ResourceLoader()).filterAbovePriority(RulePriority.LOW).warnDeprecated(true).enableCompatibility(true).createFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet(ref); assertEquals(0, ruleset.getRules().size()); @@ -1284,7 +1284,7 @@ public class RuleSetFactoryTest { } private RuleSet loadRuleSetWithDeprecationWarnings(String ruleSetXml) throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetParserConfig().warnDeprecated(true).enableCompatibility(false).createFactory(); + RuleSetFactory rsf = new RuleSetParser().warnDeprecated(true).enableCompatibility(false).createFactory(); return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml)); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java index 7954efdc88..4f8bd58215 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java @@ -9,13 +9,13 @@ import org.junit.Test; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetParserConfig; +import net.sourceforge.pmd.RuleSetParser; public class PMD5RulesetTest { @Test public void loadRuleset() throws Exception { - RuleSetFactory ruleSetFactory = new RuleSetParserConfig().createFactory(); + RuleSetFactory ruleSetFactory = new RuleSetParser().createFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet("net/sourceforge/pmd/lang/java/pmd5ruleset.xml"); Assert.assertNotNull(ruleset); Assert.assertNull(ruleset.getRuleByName("GuardLogStatementJavaUtil")); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java index 2daa2c1c39..9de80e56c1 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java @@ -16,7 +16,7 @@ import org.junit.contrib.java.lang.system.SystemErrRule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetParserConfig; +import net.sourceforge.pmd.RuleSetParser; import net.sourceforge.pmd.RuleSetNotFoundException; public class QuickstartRulesetTest { @@ -49,7 +49,7 @@ public class QuickstartRulesetTest { } }); - RuleSetFactory ruleSetFactory = new RuleSetParserConfig().enableCompatibility(false).createFactory(); + RuleSetFactory ruleSetFactory = new RuleSetParser().enableCompatibility(false).createFactory(); RuleSet quickstart = ruleSetFactory.createRuleSet("rulesets/java/quickstart.xml"); Assert.assertFalse(quickstart.getRules().isEmpty()); } From efb133c74e9d3c60ed4ff012da0000c8d43aa152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 26 Oct 2020 20:19:45 +0100 Subject: [PATCH 08/98] Cleanup API around processors --- .../main/java/net/sourceforge/pmd/PMD.java | 45 +++++++++++++++++-- .../pmd/ant/internal/PMDTaskImpl.java | 30 ++++++++----- .../pmd/processor/AbstractPMDProcessor.java | 25 ++++++++++- .../pmd/processor/MonoThreadProcessor.java | 5 ++- .../pmd/processor/MultiThreadProcessor.java | 4 ++ .../pmd/processor/PmdRunnable.java | 7 +++ .../pmd/processor/PmdThreadFactory.java | 8 ++++ 7 files changed, 107 insertions(+), 17 deletions(-) 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 cd956bb64c..516194e935 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -11,6 +11,8 @@ import java.io.Writer; import java.net.URISyntaxException; import java.sql.SQLException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; @@ -220,9 +222,9 @@ public class PMD { renderer.start(); } - RuleContext ctx = new RuleContext(); final AtomicInteger violations = new AtomicInteger(0); - ctx.getReport().addListener(new ThreadSafeReportListener() { + Report report = new Report(); + report.addListener(new ThreadSafeReportListener() { @Override public void ruleViolationAdded(RuleViolation ruleViolation) { violations.getAndIncrement(); @@ -235,7 +237,7 @@ public class PMD { }); try (TimedOperation to = TimeTracker.startOperation(TimedOperationCategory.FILE_PROCESSING)) { - processFiles(configuration, ruleSetFactory, files, ctx, renderers); + processFiles(configuration, Arrays.asList(ruleSets.getAllRuleSets()), files, report, renderers); } try (TimedOperation rto = TimeTracker.startOperation(TimedOperationCategory.REPORTING)) { @@ -296,9 +298,14 @@ public class PMD { * RuleContext * @param renderers * List of {@link Renderer}s + * + * @deprecated Use {@link #processFiles(PMDConfiguration, List, Collection, Report, List)} + * so as not to depend on {@link RuleSetFactory}. Note that this sorts the list of data sources in-place, + * which won't be fixed */ + @Deprecated public static void processFiles(final PMDConfiguration configuration, final RuleSetFactory ruleSetFactory, - final List files, final RuleContext ctx, final List renderers) { + final List files, final RuleContext ctx, final List renderers) { encourageToUseIncrementalAnalysis(configuration); sortFiles(configuration, files); // Make sure the cache is listening for analysis results @@ -309,6 +316,36 @@ public class PMD { configuration.getAnalysisCache().persist(); } + /** + * 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 parameter + * @param rulesets Parsed rulesets + * @param files Files to process + * @param report Report in which violations are accumulated + * @param renderers Renderers that render the report + * + * + * @throws RuntimeException If processing fails + */ + public static void processFiles(final PMDConfiguration configuration, + final List rulesets, + final Collection files, + final Report report, + final List renderers) { + encourageToUseIncrementalAnalysis(configuration); + report.addListener(configuration.getAnalysisCache()); + + List sortedFiles = new ArrayList<>(files); + sortFiles(configuration, sortedFiles); + + RuleContext ctx = new RuleContext(); + ctx.setReport(report); + newFileProcessor(configuration).processFiles(new RuleSets(rulesets), sortedFiles, ctx, renderers); + configuration.getAnalysisCache().persist(); + } + private static void sortFiles(final PMDConfiguration configuration, final List files) { if (configuration.isStressTest()) { // randomize processing order 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 f495cdfd34..7756e5eace 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 @@ -14,6 +14,7 @@ import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ContextedRuntimeException; import org.apache.tools.ant.AntClassLoader; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; @@ -25,7 +26,6 @@ import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetNotFoundException; @@ -104,15 +104,17 @@ public class PMDTaskImpl { RuleSetParser rulesetParser = RuleSetParser.fromPmdConfig(configuration) .loadResourcesWith(setupResourceLoader()); + List ruleSets; + try { // This is just used to validate and display rules. Each thread will create its own ruleset - String ruleSets = configuration.getRuleSets(); - if (StringUtils.isNotBlank(ruleSets)) { + String ruleSetString = configuration.getRuleSets(); + if (StringUtils.isNotBlank(ruleSetString)) { // Substitute env variables/properties - configuration.setRuleSets(project.replaceProperties(ruleSets)); + configuration.setRuleSets(project.replaceProperties(ruleSetString)); } - List rules = rulesetParser.parseFromResourceReference(configuration.getRuleSets()); - logRulesUsed(new RuleSets(rules)); + ruleSets = rulesetParser.parseFromResourceReference(configuration.getRuleSets()); + logRulesUsed(new RuleSets(ruleSets)); } catch (RuleSetNotFoundException e) { throw new BuildException(e.getMessage(), e); } @@ -132,7 +134,7 @@ public class PMDTaskImpl { // TODO Do we really need all this in a loop over each FileSet? Seems // like a lot of redundancy - RuleContext ctx = new RuleContext(); + Report report = new Report(); Report errorReport = new Report(); final AtomicInteger reportSize = new AtomicInteger(); final String separator = System.getProperty("file.separator"); @@ -191,9 +193,15 @@ public class PMDTaskImpl { renderers.add(renderer); } try { - PMD.processFiles(configuration, rulesetParser.createFactory(), files, ctx, renderers); + PMD.processFiles(configuration, ruleSets, files, report, renderers); + } catch (ContextedRuntimeException e) { + if (e.getFirstContextValue("filename") instanceof String) { + handleError((String) e.getFirstContextValue("filename"), errorReport, e); + } else { + handleError("(unknown file)", errorReport, e); + } } catch (RuntimeException pmde) { - handleError(ctx, errorReport, pmde); + handleError("(unknown file)", errorReport, pmde); } } @@ -235,7 +243,7 @@ public class PMDTaskImpl { project, classpath, parentFirst); } - private void handleError(RuleContext ctx, Report errorReport, RuntimeException pmde) { + private void handleError(String filename, Report errorReport, RuntimeException pmde) { pmde.printStackTrace(); project.log(pmde.toString(), Project.MSG_VERBOSE); @@ -258,7 +266,7 @@ public class PMDTaskImpl { if (failOnError) { throw new BuildException(pmde); } - errorReport.addError(new Report.ProcessingError(pmde, String.valueOf(ctx.getSourceCodeFile()))); + errorReport.addError(new Report.ProcessingError(pmde, filename)); } private void setupClassLoader() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java index 07552dd3e8..ff192c2bb6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java @@ -12,6 +12,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.exception.ContextedRuntimeException; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Report; @@ -21,6 +22,7 @@ import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.SourceCodeProcessor; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; import net.sourceforge.pmd.benchmark.TimedOperationCategory; @@ -30,7 +32,10 @@ import net.sourceforge.pmd.util.datasource.DataSource; /** * @author Romain Pelisse <belaran@gmail.com> * + * @deprecated Is internal API */ +@Deprecated +@InternalApi public abstract class AbstractPMDProcessor { private static final Logger LOG = Logger.getLogger(AbstractPMDProcessor.class.getName()); @@ -108,10 +113,26 @@ public abstract class AbstractPMDProcessor { @SuppressWarnings("PMD.CloseResource") // the data sources must only be closed after the threads are finished // this is done manually without a try-with-resources + @Deprecated public void processFiles(RuleSetFactory ruleSetFactory, List files, RuleContext ctx, - List renderers) { + List renderers) { try { final RuleSets rs = createRuleSets(ruleSetFactory, ctx.getReport()); + processFiles(rs, files, ctx, renderers); + } finally { + // in case we analyzed files within Zip Files/Jars, we need to close them after + // the analysis is finished + for (DataSource dataSource : files) { + IOUtils.closeQuietly(dataSource); + } + } + } + + @SuppressWarnings("PMD.CloseResource") + // the data sources must only be closed after the threads are finished + // this is done manually without a try-with-resources + public void processFiles(RuleSets rs, List files, RuleContext ctx, List renderers) { + try { configuration.getAnalysisCache().checkValidity(rs, configuration.getClassLoader()); final SourceCodeProcessor processor = new SourceCodeProcessor(configuration); @@ -127,6 +148,8 @@ public abstract class AbstractPMDProcessor { // then add analysis results per file collectReports(renderers); + } catch (RuntimeException e) { + throw new ContextedRuntimeException(e).addContextValue("filename", String.valueOf(ctx.getSourceCodeFile())); } finally { // in case we analyzed files within Zip Files/Jars, we need to close them after // the analysis is finished diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/MonoThreadProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/MonoThreadProcessor.java index 6acf61a32c..f2776574e4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/MonoThreadProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/MonoThreadProcessor.java @@ -9,12 +9,15 @@ import java.util.List; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Report; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.renderers.Renderer; /** * @author Romain Pelisse <belaran@gmail.com> - * + * @deprecated Is internal API */ +@Deprecated +@InternalApi public final class MonoThreadProcessor extends AbstractPMDProcessor { private final List reports = new ArrayList<>(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/MultiThreadProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/MultiThreadProcessor.java index f398a90db8..8745f99bf0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/MultiThreadProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/MultiThreadProcessor.java @@ -13,12 +13,16 @@ import java.util.concurrent.Executors; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Report; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.renderers.Renderer; /** * @author Romain Pelisse <belaran@gmail.com> + * @deprecated Is internal API */ +@Deprecated +@InternalApi public class MultiThreadProcessor extends AbstractPMDProcessor { private final ExecutorService executor; private final CompletionService completionService; 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 d3ddeb6230..4c290a61b6 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 @@ -17,10 +17,17 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.SourceCodeProcessor; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.util.datasource.DataSource; +/** + * + * @deprecated Is internal API + */ +@Deprecated +@InternalApi public class PmdRunnable implements Callable { private static final Logger LOG = Logger.getLogger(PmdRunnable.class.getName()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdThreadFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdThreadFactory.java index 51c84b3063..e767b8e79b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdThreadFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdThreadFactory.java @@ -7,6 +7,14 @@ package net.sourceforge.pmd.processor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; +import net.sourceforge.pmd.annotation.InternalApi; + +/** + * + * @deprecated Is internal API + */ +@Deprecated +@InternalApi public class PmdThreadFactory implements ThreadFactory { private final AtomicInteger counter = new AtomicInteger(); From 8295dad86caa23a7718a8ee863c8cfd8f9e8247e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 26 Oct 2020 20:27:38 +0100 Subject: [PATCH 09/98] Deprecate some methods of PMD --- .../main/java/net/sourceforge/pmd/PMD.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) 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 516194e935..13d4d8e2d7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -90,7 +90,10 @@ public class PMD { /** * Create a PMD instance using a default Configuration. Changes to the * configuration may be required. + * + * @deprecated Just use the static methods, and maintain your {@link PMDConfiguration} separately. */ + @Deprecated public PMD() { this(new PMDConfiguration()); } @@ -98,9 +101,11 @@ public class PMD { /** * Create a PMD instance using the specified Configuration. * - * @param configuration - * The runtime Configuration of PMD to use. + * @param configuration The runtime Configuration of PMD to use. + * + * @deprecated Just use the static methods, and maintain your {@link PMDConfiguration} separately. */ + @Deprecated public PMD(PMDConfiguration configuration) { this.configuration = configuration; this.rulesetsFileProcessor = new SourceCodeProcessor(configuration); @@ -189,7 +194,9 @@ public class PMD { * Gets the source code processor. * * @return SourceCodeProcessor + * @deprecated Source code processor is internal */ + @Deprecated public SourceCodeProcessor getSourceCodeProcessor() { return rulesetsFileProcessor; } @@ -275,7 +282,10 @@ public class PMD { * @param sourceCodeFile * the source code file * @return the rule context + * + * @deprecated Not useful */ + @Deprecated public static RuleContext newRuleContext(String sourceCodeFilename, File sourceCodeFile) { RuleContext context = new RuleContext(); @@ -319,14 +329,14 @@ public class PMD { /** * 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 parameter + * @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 * @param report Report in which violations are accumulated * @param renderers Renderers that render the report * - * * @throws RuntimeException If processing fails */ public static void processFiles(final PMDConfiguration configuration, From 125b0200ed189d8ef8f74e33e45b2ff65e69884d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 26 Oct 2020 20:30:29 +0100 Subject: [PATCH 10/98] Deprecate configuration objects --- pmd-core/src/main/java/net/sourceforge/pmd/PMD.java | 13 +++++++++++-- .../main/java/net/sourceforge/pmd/PMDException.java | 1 + .../net/sourceforge/pmd/SourceCodeProcessor.java | 6 ++++++ .../pmd/cli/PMDCommandLineInterface.java | 4 ++++ .../java/net/sourceforge/pmd/cli/PMDParameters.java | 7 +++++++ 5 files changed, 29 insertions(+), 2 deletions(-) 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 13d4d8e2d7..867343372f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -23,6 +23,7 @@ import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.benchmark.TextTimingReportRenderer; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; @@ -121,7 +122,10 @@ public class PMD { * @throws PMDException * 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 PMDException { List dataSources = new ArrayList<>(); @@ -167,7 +171,11 @@ public class PMD { * @param configuration * the given configuration * @return the pre-configured parser + * + * @deprecated This is internal */ + @Deprecated + @InternalApi public static Parser parserFor(LanguageVersion languageVersion, PMDConfiguration configuration) { // TODO Handle Rules having different parser options. @@ -488,7 +496,7 @@ public class PMD { } /** - * Entry to invoke PMD as command line tool + * Entry to invoke PMD as command line tool. Note that this will invoke {@link System#exit(int)}. * * @param args * command line arguments @@ -498,7 +506,8 @@ public class PMD { } /** - * Parses the command line arguments and executes PMD. + * Parses the command line arguments and executes PMD. Returns the + * exit code without exiting the VM. * * @param args * command line arguments diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDException.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDException.java index 4cadd9728d..d2ab5fc2b3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDException.java @@ -13,6 +13,7 @@ package net.sourceforge.pmd; * @version $Revision$, $Date$ * @since August 30, 2002 */ +@Deprecated public class PMDException extends Exception { private static final long serialVersionUID = 6938647389367956874L; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java index 4722c89ec0..235e8faa9d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java @@ -11,6 +11,7 @@ import java.io.Reader; import java.util.Collections; import java.util.List; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; import net.sourceforge.pmd.benchmark.TimedOperationCategory; @@ -23,6 +24,11 @@ import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.xpath.Initializer; +/** + * Source code processor is internal. + */ +@Deprecated +@InternalApi public class SourceCodeProcessor { private final PMDConfiguration configuration; 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 04daf8b5dc..f3ec3345bf 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 net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMDVersion; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.renderers.Renderer; @@ -19,7 +20,10 @@ import com.beust.jcommander.ParameterException; /** * @author Romain Pelisse <belaran@gmail.com> * + * @deprecated Internal API. Use {@link PMD#run(String[])} or {@link PMD#run(String[])}. */ +@Deprecated +@InternalApi public final class PMDCommandLineInterface { public static final String PROG_NAME = "pmd"; 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 b68cb336a3..e64637fc68 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 @@ -9,8 +9,10 @@ import java.util.ArrayList; import java.util.List; import java.util.Properties; +import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.RulePriority; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; @@ -20,6 +22,11 @@ import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; import com.beust.jcommander.validators.PositiveInteger; +/** + * @deprecated Internal API. Use {@link PMD#main(String[])}. + */ +@Deprecated +@InternalApi public class PMDParameters { @Parameter(names = { "-rulesets", "-R" }, description = "Comma separated list of ruleset names to use.", From 7d66d7cbca6f88f80d4468f412f93950ca121014 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 26 Oct 2020 20:41:36 +0100 Subject: [PATCH 11/98] Doc --- docs/pages/release_notes.md | 17 +++++++++++++++++ .../src/main/java/net/sourceforge/pmd/PMD.java | 7 +++++++ .../java/net/sourceforge/pmd/RuleSetParser.java | 13 ++++++++++++- .../net/sourceforge/pmd/RuleSetReference.java | 6 ++++++ .../pmd/cli/PMDCommandLineInterface.java | 2 +- .../net/sourceforge/pmd/cli/PMDParameters.java | 2 +- 6 files changed, 44 insertions(+), 3 deletions(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b8f8783555..710428b79d 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,6 +18,23 @@ This is a {{ site.pmd.release_type }} release. ### API Changes +#### Deprecated API + +##### Around RuleSet parsing + +* {% jdoc core::RuleSetFactory %} and {% jdoc core::RuleSetFactoryUtils %} have been deprecated in favor of {% jdoc core::RuleSetParser %}. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. +* Some static creation methods have been added to {% jdoc core::RuleSet %} for simple cases, eg {% jdoc core::RuleSet#forSingleRule(core::Rule) %}. These replace some counterparts in {% jdoc core::RuleSetFactory %} +* Since {% jdoc core::RuleSets %} is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. +* {% jdoc core::RuleSetReferenceId %}, {% jdoc core::RuleSetReference %}, {% jdoc core::RuleSetFactoryCompatibility %} are deprecated. They are most likely not relevant outside of the implementation of pmd-core. + +##### Around the `PMD` class + +Many APIs around PMD's entry point ({% jdoc core::PMD %}) have been deprecated, including: +* The contents of the packages {% jdoc_package core::cli %}, {% jdoc_package core::processor %} +* {% jdoc core::SourceCodeProcessor %} +* The constructors of {% jdoc core::PMD %} (the class will be made a utility class) + + ### External Contributions {% endtocmaker %} 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 867343372f..161dc9d48a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -193,7 +193,10 @@ public class PMD { * * @return The configuration. * @see PMDConfiguration + * + * @deprecated Don't create a PMD instance just to create a {@link PMDConfiguration} */ + @Deprecated public PMDConfiguration getConfiguration() { return configuration; } @@ -324,6 +327,10 @@ public class PMD { @Deprecated public static void processFiles(final PMDConfiguration configuration, final RuleSetFactory ruleSetFactory, final List files, final RuleContext ctx, final List renderers) { + // Note that this duplicates the other routine, because the old behavior was + // that we parsed rulesets (a second time) inside the processor execution. + // To not mess up error handling, we keep this behavior. + encourageToUseIncrementalAnalysis(configuration); sortFiles(configuration, files); // Make sure the cache is listening for analysis results diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java index f972e541fb..9d9173b318 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java @@ -11,7 +11,9 @@ import net.sourceforge.pmd.util.ResourceLoader; /** * Configurable ruleset parser. Note that this replaces the API of {@link RulesetsFactoryUtils} - * and {@link RuleSetFactory}. + * and {@link RuleSetFactory}. This can be configured using a fluent + * API, see eg {@link #warnDeprecated(boolean)}. To create a list of + * rulesets, use {@link #parseFromResourceReference(String)}. */ public final class RuleSetParser { @@ -48,6 +50,7 @@ public final class RuleSetParser { * Filter loaded rules to only those that match or are above * the given priority. The default is {@link RulePriority#LOW}, * ie, no filtering occurs. + * @return This instance, modified */ public RuleSetParser filterAbovePriority(RulePriority minimumPriority) { this.minimumPriority = minimumPriority; @@ -57,6 +60,7 @@ public final class RuleSetParser { /** * Log a warning when referencing a deprecated rule. * This is enabled by default. + * @return This instance, modified */ public RuleSetParser warnDeprecated(boolean warn) { this.warnDeprecated = warn; @@ -68,6 +72,7 @@ public final class RuleSetParser { * been moved or renamed. This is enabled by default, if disabled, * unresolved references will not be translated and will produce an * error. + * @return This instance, modified */ public RuleSetParser enableCompatibility(boolean enable) { this.enableCompatibility = enable; @@ -78,12 +83,18 @@ public final class RuleSetParser { * Follow deprecated rule references. By default this is off, * and those references will be ignored (with a warning depending * on {@link #enableCompatibility(boolean)}). + * + * @return This instance, modified */ public RuleSetParser includeDeprecatedRuleReferences(boolean enable) { this.includeDeprecatedRuleReferences = enable; return this; } + /** + * Create a new rule set factory, if you have to (that class is deprecated). + * That factory will use the configuration that was set using the setters of this. + */ public RuleSetFactory createFactory() { return new RuleSetFactory(this); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReference.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReference.java index e7098568e1..32e2eeedb7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReference.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReference.java @@ -8,9 +8,15 @@ import java.util.Collections; import java.util.LinkedHashSet; import java.util.Set; +import net.sourceforge.pmd.annotation.InternalApi; + /** * This class represents a reference to RuleSet. + * + * @deprecated This is part of the internals of the {@link RuleSetParser}. */ +@Deprecated +@InternalApi public class RuleSetReference { private final String ruleSetFileName; private final boolean allRules; 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 f3ec3345bf..a8ad35ed4a 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 @@ -20,7 +20,7 @@ import com.beust.jcommander.ParameterException; /** * @author Romain Pelisse <belaran@gmail.com> * - * @deprecated Internal API. Use {@link PMD#run(String[])} or {@link PMD#run(String[])}. + * @deprecated Internal API. Use {@link PMD#run(String[])} or {@link PMD#main(String[])} */ @Deprecated @InternalApi 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 e64637fc68..94cd430553 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 @@ -23,7 +23,7 @@ import com.beust.jcommander.ParameterException; import com.beust.jcommander.validators.PositiveInteger; /** - * @deprecated Internal API. Use {@link PMD#main(String[])}. + * @deprecated Internal API. Use {@link PMD#run(String[])} or {@link PMD#main(String[])} */ @Deprecated @InternalApi From 8f8af7780e092f3eb371e9bf34e15168bc139d4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 26 Oct 2020 21:10:58 +0100 Subject: [PATCH 12/98] Rename stuff --- .../pmd/lang/apex/DefaultRulesetTest.java | 2 +- .../main/java/net/sourceforge/pmd/PMD.java | 4 +-- .../net/sourceforge/pmd/RuleSetFactory.java | 15 +++++++---- .../net/sourceforge/pmd/RuleSetParser.java | 3 ++- .../sourceforge/pmd/RuleSetFactoryTest.java | 26 +++++++++---------- .../pmd/lang/java/PMD5RulesetTest.java | 2 +- .../pmd/lang/java/QuickstartRulesetTest.java | 2 +- 7 files changed, 30 insertions(+), 24 deletions(-) diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java index ca06fce388..8a1c2db3c1 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java @@ -22,7 +22,7 @@ public class DefaultRulesetTest { @Rule public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests(); - private RuleSetFactory factory = new RuleSetParser().enableCompatibility(false).createFactory(); + private RuleSetFactory factory = new RuleSetParser().enableCompatibility(false).toFactory(); @Test public void loadDefaultRuleset() throws Exception { 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 161dc9d48a..370c0ad672 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -222,7 +222,7 @@ public class PMD { public static int doPMD(PMDConfiguration configuration) { // Load the RuleSets - final RuleSetFactory ruleSetFactory = RuleSetParser.fromPmdConfig(configuration).createFactory(); + final RuleSetFactory ruleSetFactory = RuleSetParser.fromPmdConfig(configuration).toFactory(); final RuleSets ruleSets = RulesetsFactoryUtils.getRuleSetsWithBenchmark(configuration.getRuleSets(), ruleSetFactory); if (ruleSets == null) { return PMDCommandLineInterface.NO_ERRORS_STATUS; @@ -336,7 +336,7 @@ public class PMD { // Make sure the cache is listening for analysis results ctx.getReport().addListener(configuration.getAnalysisCache()); - final RuleSetFactory silentFactory = ruleSetFactory.toConfig().warnDeprecated(false).createFactory(); + final RuleSetFactory silentFactory = ruleSetFactory.toParser().warnDeprecated(false).toFactory(); newFileProcessor(configuration).processFiles(silentFactory, files, ctx, renderers); configuration.getAnalysisCache().persist(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 8200c6d525..ef578dab76 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -115,7 +115,7 @@ public class RuleSetFactory { * Whether deprecation warnings are to be produced by this * factory * - * @deprecated Use {@link #toConfig()} to rebuild a factory from a configuration + * @deprecated Use {@link #toParser()} to rebuild a factory from a configuration */ @Deprecated public RuleSetFactory(final RuleSetFactory factory, final boolean warnDeprecated) { @@ -566,7 +566,7 @@ public class RuleSetFactory { // load the ruleset with minimum priority low, so that we get all rules, to be able to exclude any rule // minimum priority will be applied again, before constructing the final ruleset - RuleSetFactory ruleSetFactory = toConfig().filterAbovePriority(RulePriority.LOW).warnDeprecated(false).createFactory(); + RuleSetFactory ruleSetFactory = toParser().filterAbovePriority(RulePriority.LOW).warnDeprecated(false).toFactory(); RuleSet otherRuleSet = ruleSetFactory.createRuleSet(RuleSetReferenceId.parse(ref).get(0)); List potentialRules = new ArrayList<>(); int countDeprecated = 0; @@ -681,7 +681,7 @@ public class RuleSetFactory { // load the ruleset with minimum priority low, so that we get all rules, to be able to exclude any rule // minimum priority will be applied again, before constructing the final ruleset - RuleSetFactory ruleSetFactory = toConfig().warnDeprecated(false).createFactory(); + RuleSetFactory ruleSetFactory = toParser().warnDeprecated(false).toFactory(); boolean isSameRuleSet = false; RuleSetReferenceId otherRuleSetReferenceId = RuleSetReferenceId.parse(ref).get(0); @@ -843,11 +843,16 @@ public class RuleSetFactory { } - public RuleSetParser toConfig() { + /** + * Create a new {@link RuleSetParser} with the same config as this + * factory. This is a transitional API. + */ + public RuleSetParser toParser() { return new RuleSetParser().loadResourcesWith(resourceLoader) .filterAbovePriority(minimumPriority) .warnDeprecated(warnDeprecated) - .enableCompatibility(compatibilityFilter != null); + .enableCompatibility(compatibilityFilter != null) + .includeDeprecatedRuleReferences(includeDeprecatedRuleReferences); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java index 9d9173b318..326c5b0914 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java @@ -95,7 +95,8 @@ public final class RuleSetParser { * Create a new rule set factory, if you have to (that class is deprecated). * That factory will use the configuration that was set using the setters of this. */ - public RuleSetFactory createFactory() { + @Deprecated + public RuleSetFactory toFactory() { return new RuleSetFactory(this); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 60e2c8ea63..455d0287b9 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -507,38 +507,38 @@ public class RuleSetFactoryTest { public void testReferencePriority() throws RuleSetNotFoundException { RuleSetParser config = new RuleSetParser().warnDeprecated(false).enableCompatibility(true); - RuleSetFactory rsf = config.filterAbovePriority(RulePriority.LOW).createFactory(); + RuleSetFactory rsf = config.filterAbovePriority(RulePriority.LOW).toFactory(); RuleSet ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); assertEquals("Number of Rules", 3, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("MockRuleName")); assertNotNull(ruleSet.getRuleByName("MockRuleNameRef")); assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - rsf = config.filterAbovePriority(RulePriority.MEDIUM_HIGH).createFactory(); + rsf = config.filterAbovePriority(RulePriority.MEDIUM_HIGH).toFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); assertEquals("Number of Rules", 2, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("MockRuleNameRef")); assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - rsf = config.filterAbovePriority(RulePriority.HIGH).createFactory(); + rsf = config.filterAbovePriority(RulePriority.HIGH).toFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); assertEquals("Number of Rules", 1, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - rsf = config.filterAbovePriority(RulePriority.LOW).createFactory(); + rsf = config.filterAbovePriority(RulePriority.LOW).toFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN)); assertEquals("Number of Rules", 3, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleName")); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRef")); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); - rsf = config.filterAbovePriority(RulePriority.MEDIUM_HIGH).createFactory(); + rsf = config.filterAbovePriority(RulePriority.MEDIUM_HIGH).toFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN)); assertEquals("Number of Rules", 2, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRef")); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); - rsf = config.filterAbovePriority(RulePriority.HIGH).createFactory(); + rsf = config.filterAbovePriority(RulePriority.HIGH).toFactory(); ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN)); assertEquals("Number of Rules", 1, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); @@ -546,7 +546,7 @@ public class RuleSetFactoryTest { @Test public void testOverridePriorityLoadWithMinimum() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetParser().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true).createFactory(); + RuleSetFactory rsf = new RuleSetParser().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true).toFactory(); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); // only one rule should remain, since we filter out the other rule by minimum priority assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -567,13 +567,13 @@ public class RuleSetFactoryTest { @Test public void testExcludeWithMinimumPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetParser().filterAbovePriority(RulePriority.HIGH).createFactory(); + RuleSetFactory rsf = new RuleSetParser().filterAbovePriority(RulePriority.HIGH).toFactory(); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // no rules should be loaded assertEquals("Number of Rules", 0, ruleset.getRules().size()); // now, load with default minimum priority - rsf = new RuleSetParser().filterAbovePriority(RulePriority.LOW).createFactory(); + rsf = new RuleSetParser().filterAbovePriority(RulePriority.LOW).toFactory(); ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // only one rule, we have excluded one... assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -602,9 +602,9 @@ public class RuleSetFactoryTest { @Test public void testSetPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetParser().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false).createFactory(); + RuleSetFactory rsf = new RuleSetParser().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false).toFactory(); assertEquals(0, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); - rsf = new RuleSetParser().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false).createFactory(); + rsf = new RuleSetParser().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false).toFactory(); assertEquals(1, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); } @@ -779,7 +779,7 @@ public class RuleSetFactoryTest { + " Ruleset which references a empty ruleset\n" + "\n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetParser().loadResourcesWith(new ResourceLoader()).filterAbovePriority(RulePriority.LOW).warnDeprecated(true).enableCompatibility(true).createFactory(); + RuleSetFactory ruleSetFactory = new RuleSetParser().loadResourcesWith(new ResourceLoader()).filterAbovePriority(RulePriority.LOW).warnDeprecated(true).enableCompatibility(true).toFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet(ref); assertEquals(0, ruleset.getRules().size()); @@ -1284,7 +1284,7 @@ public class RuleSetFactoryTest { } private RuleSet loadRuleSetWithDeprecationWarnings(String ruleSetXml) throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetParser().warnDeprecated(true).enableCompatibility(false).createFactory(); + RuleSetFactory rsf = new RuleSetParser().warnDeprecated(true).enableCompatibility(false).toFactory(); return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml)); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java index 4f8bd58215..d4406a6ed0 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java @@ -15,7 +15,7 @@ public class PMD5RulesetTest { @Test public void loadRuleset() throws Exception { - RuleSetFactory ruleSetFactory = new RuleSetParser().createFactory(); + RuleSetFactory ruleSetFactory = new RuleSetParser().toFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet("net/sourceforge/pmd/lang/java/pmd5ruleset.xml"); Assert.assertNotNull(ruleset); Assert.assertNull(ruleset.getRuleByName("GuardLogStatementJavaUtil")); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java index 9de80e56c1..9b356fe03c 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java @@ -49,7 +49,7 @@ public class QuickstartRulesetTest { } }); - RuleSetFactory ruleSetFactory = new RuleSetParser().enableCompatibility(false).createFactory(); + RuleSetFactory ruleSetFactory = new RuleSetParser().enableCompatibility(false).toFactory(); RuleSet quickstart = ruleSetFactory.createRuleSet("rulesets/java/quickstart.xml"); Assert.assertFalse(quickstart.getRules().isEmpty()); } From 9c56fb629a5330858c36c561bf9b6bd1553db051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 26 Oct 2020 21:21:29 +0100 Subject: [PATCH 13/98] Fix bug --- pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index ef578dab76..99eef0b567 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -681,7 +681,7 @@ public class RuleSetFactory { // load the ruleset with minimum priority low, so that we get all rules, to be able to exclude any rule // minimum priority will be applied again, before constructing the final ruleset - RuleSetFactory ruleSetFactory = toParser().warnDeprecated(false).toFactory(); + RuleSetFactory ruleSetFactory = toParser().filterAbovePriority(RulePriority.LOW).warnDeprecated(false).toFactory(); boolean isSameRuleSet = false; RuleSetReferenceId otherRuleSetReferenceId = RuleSetReferenceId.parse(ref).get(0); From 38a0934feef8900fafe04c19ef7437ed4106832b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 26 Oct 2020 21:32:26 +0100 Subject: [PATCH 14/98] PMD warnings --- .../src/main/java/net/sourceforge/pmd/RuleSetFactory.java | 7 ++++--- .../src/main/java/net/sourceforge/pmd/RuleSetParser.java | 7 ------- .../java/net/sourceforge/pmd/benchmark/Benchmarker.java | 1 - 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 99eef0b567..c8ad55f0c7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -163,9 +163,10 @@ public class RuleSetFactory { } catch (RuleSetNotFoundException e) { LOG.warning("The language " + language.getTerseName() + " provides no " + rulesetsProperties + "."); } catch (IOException ioe) { - throw new RuntimeException("Couldn't find " + rulesetsProperties - + "; please ensure that the directory is on the classpath. The current classpath is: " - + System.getProperty("java.class.path"));} + throw new RuntimeException("Couldn't find " + rulesetsProperties + + "; please ensure that the directory is on the classpath. The current classpath is: " + + System.getProperty("java.class.path")); + } } return createRuleSets(ruleSetReferenceIds).getRuleSetsIterator(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java index 326c5b0914..815b58056a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java @@ -23,13 +23,6 @@ public final class RuleSetParser { boolean enableCompatibility = true; boolean includeDeprecatedRuleReferences = false; - /** - * Create a new config with the default values. - */ - public RuleSetParser() { - - } - /** * Specify that the given classloader should be used to resolve * paths to external ruleset references. The default uses PMD's diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java b/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java index 89a81b28e0..af56e1d1e0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java @@ -175,7 +175,6 @@ public final class Benchmarker { private static void stress(LanguageVersion languageVersion, RuleSet ruleSet, List dataSources, Set results, boolean debug) throws PMDException, IOException { - final RuleSetFactory factory = RulesetsFactoryUtils.defaultFactory(); for (Rule rule: ruleSet.getRules()) { if (debug) { System.out.println("Starting " + rule.getName()); From 050ec4464e56405946674dad2f979cb6eeb21f0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 26 Oct 2020 23:30:03 +0100 Subject: [PATCH 15/98] Stop parsing comma-separated paths by default --- .../net/sourceforge/pmd/RuleSetFactory.java | 5 +- .../net/sourceforge/pmd/RuleSetParser.java | 52 +++++++++++++------ .../pmd/ant/internal/PMDTaskImpl.java | 31 +++++------ 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index c8ad55f0c7..0778035fc3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -184,7 +184,8 @@ public class RuleSetFactory { * @throws RuleSetNotFoundException * if unable to find a resource. * - * @deprecated Use {@link RuleSetParser#parseFromResourceReference(String)} + * @deprecated Use {@link RuleSetParser#parseFromResource(String)}, + * but note that that method does not split on commas */ @Deprecated public RuleSets createRuleSets(String referenceString) throws RuleSetNotFoundException { @@ -226,7 +227,7 @@ public class RuleSetFactory { * @throws RuleSetNotFoundException * if unable to find a resource. * - * @deprecated Use {@link RuleSetParser#parseFromResourceReference(String)} and discard the rest of the list. + * @deprecated Use {@link RuleSetParser#parseFromResource(String)} and discard the rest of the list. */ @Deprecated public RuleSet createRuleSet(String referenceString) throws RuleSetNotFoundException { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java index 815b58056a..72b3dc60b7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java @@ -5,6 +5,8 @@ package net.sourceforge.pmd; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import net.sourceforge.pmd.util.ResourceLoader; @@ -13,7 +15,7 @@ import net.sourceforge.pmd.util.ResourceLoader; * Configurable ruleset parser. Note that this replaces the API of {@link RulesetsFactoryUtils} * and {@link RuleSetFactory}. This can be configured using a fluent * API, see eg {@link #warnDeprecated(boolean)}. To create a list of - * rulesets, use {@link #parseFromResourceReference(String)}. + * rulesets, use {@link #parseFromResource(String)}. */ public final class RuleSetParser { @@ -95,32 +97,50 @@ public final class RuleSetParser { /** - * Create a RuleSets from a comma separated list of RuleSet reference IDs. - * This is a convenience method which calls - * {@link RuleSetReferenceId#parse(String)}, and then calls - * {@link #createRuleSets(List)}. The currently configured ResourceLoader is - * used. + * Parses and returns a ruleset from its location. The location may + * be a file system path, or a resource path (see {@link #loadResourcesWith(ClassLoader)}). * - * @param rulesetResourcePaths A comma separated list of RuleSet reference IDs. + *

This replaces {@link RuleSetFactory#createRuleSet(String)}, + * but does not split commas. * - * @return The new RuleSets. + * @param rulesetPath A reference to a single ruleset + * + * @throws RuleSetNotFoundException If the path does not correspond to a resource */ - public List parseFromResourceReference(String rulesetResourcePaths) throws RuleSetNotFoundException { - return createRuleSets(RuleSetReferenceId.parse(rulesetResourcePaths)); + public RuleSet parseFromResource(String rulesetPath) throws RuleSetNotFoundException { + return parseFromResource(new RuleSetReferenceId(rulesetPath)); } - private List createRuleSets(List ruleSetReferenceIds) throws RuleSetNotFoundException { - List ruleSets = new ArrayList<>(); - for (RuleSetReferenceId ruleSetReferenceId : ruleSetReferenceIds) { - RuleSet ruleSet = createRuleSet(ruleSetReferenceId); - ruleSets.add(ruleSet); + /** + * Parses several resources into a list of rulesets. + * + * @param paths Paths + * + * @throws RuleSetNotFoundException If any resource throws + * @throws NullPointerException If the parameter, or any component is null + */ + public List parseFromResources(Collection paths) throws RuleSetNotFoundException { + List ruleSets = new ArrayList<>(paths.size()); + for (String path : paths) { + ruleSets.add(parseFromResource(path)); } return ruleSets; } + /** + * Parses several resources into a list of rulesets. + * + * @param paths Paths + * + * @throws RuleSetNotFoundException If any resource throws + * @throws NullPointerException If the parameter, or any component is null + */ + public List parseFromResources(String... paths) throws RuleSetNotFoundException { + return parseFromResources(Arrays.asList(paths)); + } // package private - RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { + RuleSet parseFromResource(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { return new RuleSetFactory(this).createRuleSet(ruleSetReferenceId); } 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 7756e5eace..101bd4d8ec 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 @@ -9,6 +9,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -28,9 +29,9 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetNotFoundException; import net.sourceforge.pmd.RuleSetParser; import net.sourceforge.pmd.RuleSets; +import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.ant.Formatter; import net.sourceforge.pmd.ant.PMDTask; import net.sourceforge.pmd.ant.SourceLanguage; @@ -104,21 +105,17 @@ public class PMDTaskImpl { RuleSetParser rulesetParser = RuleSetParser.fromPmdConfig(configuration) .loadResourcesWith(setupResourceLoader()); - List ruleSets; - - try { - // This is just used to validate and display rules. Each thread will create its own ruleset - String ruleSetString = configuration.getRuleSets(); - if (StringUtils.isNotBlank(ruleSetString)) { - // Substitute env variables/properties - configuration.setRuleSets(project.replaceProperties(ruleSetString)); - } - ruleSets = rulesetParser.parseFromResourceReference(configuration.getRuleSets()); - logRulesUsed(new RuleSets(ruleSets)); - } catch (RuleSetNotFoundException e) { - throw new BuildException(e.getMessage(), e); + // This is just used to validate and display rules. Each thread will create its own ruleset + String ruleSetString = configuration.getRuleSets(); + if (StringUtils.isNotBlank(ruleSetString)) { + // Substitute env variables/properties + configuration.setRuleSets(project.replaceProperties(ruleSetString)); } + final RuleSets ruleSets = RulesetsFactoryUtils.getRuleSets(configuration.getRuleSets(), rulesetParser.toFactory()); + List rulesetList = Arrays.asList(ruleSets.getAllRuleSets()); + logRulesUsed(ruleSets); + if (configuration.getSuppressMarker() != null) { project.log("Setting suppress marker to be " + configuration.getSuppressMarker(), Project.MSG_VERBOSE); } @@ -193,7 +190,7 @@ public class PMDTaskImpl { renderers.add(renderer); } try { - PMD.processFiles(configuration, ruleSets, files, report, renderers); + PMD.processFiles(configuration, rulesetList, files, report, renderers); } catch (ContextedRuntimeException e) { if (e.getFirstContextValue("filename") instanceof String) { handleError((String) e.getFirstContextValue("filename"), errorReport, e); @@ -285,6 +282,10 @@ public class PMDTaskImpl { final ScopedLogHandlersManager logManager = new ScopedLogHandlersManager(antLogHandler.getAntLogLevel(), antLogHandler); try { doTask(); + } catch (BuildException e) { + throw e; + } catch (Exception other) { + throw new BuildException(other); } finally { logManager.close(); // only close the classloader, if it is ours. Otherwise we end up with class not found From cc7bb35e72ed6fa016e49dd663b0170ba5951537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 00:30:31 +0100 Subject: [PATCH 16/98] Checkstyle --- .../net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java index 9b356fe03c..270238f2a8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java @@ -16,8 +16,8 @@ import org.junit.contrib.java.lang.system.SystemErrRule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetParser; import net.sourceforge.pmd.RuleSetNotFoundException; +import net.sourceforge.pmd.RuleSetParser; public class QuickstartRulesetTest { From 36e55bc2c1d4ea0a41d39fed0b9178d876b3447f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 09:15:16 +0100 Subject: [PATCH 17/98] Fix broken rule reporting --- .../pmd/processor/AbstractPMDProcessor.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java index ff192c2bb6..d0ad3ae1ef 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java @@ -80,13 +80,15 @@ public abstract class AbstractPMDProcessor { */ protected RuleSets createRuleSets(RuleSetFactory factory, Report report) { final RuleSets rs = RulesetsFactoryUtils.getRuleSets(configuration.getRuleSets(), factory); + reportBrokenRules(report, rs); + return rs; + } + public static void reportBrokenRules(Report report, RuleSets rs) { final Set brokenRules = removeBrokenRules(rs); for (final Rule rule : brokenRules) { report.addConfigError(new Report.ConfigurationError(rule, rule.dysfunctionReason())); } - - return rs; } /** @@ -96,7 +98,7 @@ public abstract class AbstractPMDProcessor { * @param ruleSets RuleSets to prune of broken rules. * @return Set */ - private Set removeBrokenRules(final RuleSets ruleSets) { + private static Set removeBrokenRules(final RuleSets ruleSets) { final Set brokenRules = new HashSet<>(); ruleSets.removeDysfunctionalRules(brokenRules); @@ -131,16 +133,17 @@ public abstract class AbstractPMDProcessor { @SuppressWarnings("PMD.CloseResource") // the data sources must only be closed after the threads are finished // this is done manually without a try-with-resources - public void processFiles(RuleSets rs, List files, RuleContext ctx, List renderers) { + public void processFiles(RuleSets rulesets, List files, RuleContext ctx, List renderers) { try { - configuration.getAnalysisCache().checkValidity(rs, configuration.getClassLoader()); + reportBrokenRules(ctx.getReport(), rulesets); + configuration.getAnalysisCache().checkValidity(rulesets, configuration.getClassLoader()); final SourceCodeProcessor processor = new SourceCodeProcessor(configuration); for (final DataSource dataSource : files) { // this is the real, canonical and absolute filename (not shortened) String realFileName = dataSource.getNiceFileName(false, null); - runAnalysis(new PmdRunnable(dataSource, realFileName, renderers, ctx, rs, processor)); + runAnalysis(new PmdRunnable(dataSource, realFileName, renderers, ctx, rulesets, processor)); } // render base report first - general errors From f70b2e44fca1b364f14faa52a2eb456f7f2ecc76 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Tue, 27 Oct 2020 16:23:14 -0700 Subject: [PATCH 18/98] Rename ExpressionType, remove google collections Renamed ExpressionType to IdentifierType since this is more accurate. Removed usage of google.collect classes that were causing UnsupportedClassVersionError exception in the Travis CI run. --- .../pmd/lang/vf/ApexClassPropertyTypes.java | 20 +++---- .../vf/ApexClassPropertyTypesVisitor.java | 2 +- ...xpressionType.java => IdentifierType.java} | 53 +++++++++--------- .../pmd/lang/vf/ObjectFieldTypes.java | 54 +++++++++---------- .../pmd/lang/vf/VfExpressionTypeVisitor.java | 12 ++--- .../AbstractVfTypedElExpressionRule.java | 14 ++--- .../vf/rule/security/VfUnescapeElRule.java | 6 +-- .../lang/vf/ApexClassPropertyTypesTest.java | 18 +++---- .../pmd/lang/vf/ExpressionTypeTest.java | 38 ------------- .../pmd/lang/vf/IdentifierTypeTest.java | 38 +++++++++++++ .../pmd/lang/vf/ObjectFieldTypesTest.java | 24 ++++----- 11 files changed, 139 insertions(+), 140 deletions(-) rename pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/{ExpressionType.java => IdentifierType.java} (63%) delete mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ExpressionTypeTest.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/IdentifierTypeTest.java diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java index 2c2528d4b1..f82e517cb8 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java @@ -10,6 +10,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Set; @@ -27,7 +28,6 @@ import net.sourceforge.pmd.lang.apex.ast.ApexNode; import net.sourceforge.pmd.lang.ast.Node; import apex.jorje.semantic.symbol.type.BasicType; -import com.google.common.collect.Sets; /** * Responsible for storing a mapping of Apex Class properties that can be referenced from Visualforce to the type of the @@ -37,20 +37,20 @@ class ApexClassPropertyTypes { private static final Logger LOGGER = Logger.getLogger(ApexClassPropertyTypes.class.getName()); private static final String APEX_CLASS_FILE_SUFFIX = ".cls"; - private final ConcurrentHashMap variableNameToVariableType; + private final ConcurrentHashMap variableNameToVariableType; private final Set variableNameProcessed; ApexClassPropertyTypes() { this.variableNameToVariableType = new ConcurrentHashMap<>(); - this.variableNameProcessed = Sets.newConcurrentHashSet(); + this.variableNameProcessed = Collections.newSetFromMap(new ConcurrentHashMap()); } /** * Looks in {@code apexDirectories} for an Apex property identified by {@code expression}. * - * @return the ExpressionType for the property represented by {@code expression} or null if not found. + * @return the IdentifierType for the property represented by {@code expression} or null if not found. */ - public ExpressionType getVariableType(String expression, String vfFileName, List apexDirectories) { + public IdentifierType getVariableType(String expression, String vfFileName, List apexDirectories) { String lowerExpression = expression.toLowerCase(Locale.ROOT); if (variableNameToVariableType.containsKey(lowerExpression)) { // The expression has been previously retrieved @@ -103,20 +103,20 @@ class ApexClassPropertyTypes { } private void setVariableType(String name, BasicType basicType) { - ExpressionType expressionType = ExpressionType.fromBasicType(basicType); - ExpressionType previousType = variableNameToVariableType.put(name.toLowerCase(Locale.ROOT), expressionType); - if (previousType != null && !previousType.equals(expressionType)) { + IdentifierType identifierType = IdentifierType.fromBasicType(basicType); + IdentifierType previousType = variableNameToVariableType.put(name.toLowerCase(Locale.ROOT), identifierType); + if (previousType != null && !previousType.equals(identifierType)) { // It is possible to have a property and method with different types that appear the same to this code. An // example is an Apex class with a property "public String Foo {get; set;}" and a method of // "Integer getFoo() { return 1; }". In this case set the value as Unknown because we can't be sure which it // is. This code could be more complex in an attempt to determine if all the types are safe from escaping, // but we will allow a false positive in order to let the user know that the code could be refactored to be // more clear. - variableNameToVariableType.put(name.toLowerCase(Locale.ROOT), ExpressionType.Unknown); + variableNameToVariableType.put(name.toLowerCase(Locale.ROOT), IdentifierType.Unknown); LOGGER.warning("Conflicting types for " + name + ". CurrentType=" - + expressionType + + identifierType + ", PreviousType=" + previousType); } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor.java index 18fd9408c2..cee166c5e5 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor.java @@ -37,7 +37,7 @@ final class ApexClassPropertyTypesVisitor extends ApexParserVisitorAdapter { private static final String RETURN_TYPE_VOID = "void"; /** - * Pairs of (variableName, expressionType) + * Pairs of (variableName, identifierType) */ private final List> variables; diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ExpressionType.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/IdentifierType.java similarity index 63% rename from pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ExpressionType.java rename to pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/IdentifierType.java index c6d327b3ab..091a74202c 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ExpressionType.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/IdentifierType.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.lang.vf; import java.util.Arrays; +import java.util.Collections; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -12,8 +13,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import apex.jorje.semantic.symbol.type.BasicType; -import com.google.common.base.Strings; -import com.google.common.collect.Sets; /** * Represents all data types that can be referenced from a Visualforce page. This enum consolidates the data types @@ -21,7 +20,7 @@ import com.google.common.collect.Sets; * * See https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_field_types.htm#meta_type_fieldtype */ -public enum ExpressionType { +public enum IdentifierType { AutoNumber(false), Checkbox(false, BasicType.BOOLEAN), Currency(false, BasicType.CURRENCY), @@ -52,7 +51,7 @@ public enum ExpressionType { Url(false), Unknown(true); - private static final Logger LOGGER = Logger.getLogger(ExpressionType.class.getName()); + private static final Logger LOGGER = Logger.getLogger(IdentifierType.class.getName()); /** @@ -69,18 +68,18 @@ public enum ExpressionType { * A case insensitive map of the enum name to its instance. The case metadata is not guaranteed to have the correct * case. */ - private static final Map CASE_INSENSITIVE_MAP = new ConcurrentHashMap<>(); + private static final Map CASE_INSENSITIVE_MAP = new ConcurrentHashMap<>(); /** - * Map of BasicType to ExpressionType. Multiple BasicTypes may map to one ExrpessionType. + * Map of BasicType to IdentifierType. Multiple BasicTypes may map to one ExrpessionType. */ - private static final Map BASIC_TYPE_MAP = new ConcurrentHashMap<>(); + private static final Map BASIC_TYPE_MAP = new ConcurrentHashMap<>(); static { - for (ExpressionType expressionType : ExpressionType.values()) { - CASE_INSENSITIVE_MAP.put(expressionType.name().toLowerCase(Locale.ROOT), expressionType); - for (BasicType basicType : expressionType.basicTypes) { - BASIC_TYPE_MAP.put(basicType, expressionType); + for (IdentifierType identifierType : IdentifierType.values()) { + CASE_INSENSITIVE_MAP.put(identifierType.name().toLowerCase(Locale.ROOT), identifierType); + for (BasicType basicType : identifierType.basicTypes) { + BASIC_TYPE_MAP.put(basicType, identifierType); } } } @@ -88,39 +87,39 @@ public enum ExpressionType { /** * Map to correct instance, returns {@code Unknown} if the value can't be mapped. */ - public static ExpressionType fromString(String value) { - value = Strings.nullToEmpty(value); - ExpressionType expressionType = CASE_INSENSITIVE_MAP.get(value.toLowerCase(Locale.ROOT)); + public static IdentifierType fromString(String value) { + value = value != null ? value : ""; + IdentifierType identifierType = CASE_INSENSITIVE_MAP.get(value.toLowerCase(Locale.ROOT)); - if (expressionType == null) { - expressionType = ExpressionType.Unknown; - LOGGER.fine("Unable to determine ExpressionType of " + value); + if (identifierType == null) { + identifierType = IdentifierType.Unknown; + LOGGER.fine("Unable to determine IdentifierType of " + value); } - return expressionType; + return identifierType; } /** * Map to correct instance, returns {@code Unknown} if the value can't be mapped. */ - public static ExpressionType fromBasicType(BasicType value) { - ExpressionType expressionType = value != null ? BASIC_TYPE_MAP.get(value) : null; + public static IdentifierType fromBasicType(BasicType value) { + IdentifierType identifierType = value != null ? BASIC_TYPE_MAP.get(value) : null; - if (expressionType == null) { - expressionType = ExpressionType.Unknown; - LOGGER.fine("Unable to determine ExpressionType of " + value); + if (identifierType == null) { + identifierType = IdentifierType.Unknown; + LOGGER.fine("Unable to determine IdentifierType of " + value); } - return expressionType; + return identifierType; } - ExpressionType(boolean requiresEscaping) { + IdentifierType(boolean requiresEscaping) { this(requiresEscaping, null); } - ExpressionType(boolean requiresEscaping, BasicType...basicTypes) { + IdentifierType(boolean requiresEscaping, BasicType...basicTypes) { this.requiresEscaping = requiresEscaping; - this.basicTypes = Sets.newConcurrentHashSet(); + this.basicTypes = Collections.newSetFromMap(new ConcurrentHashMap()); if (basicTypes != null) { this.basicTypes.addAll(Arrays.asList(basicTypes)); } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java index 9ad7bdbe3c..1a047c6b17 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.vf; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; @@ -26,9 +27,6 @@ import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Sets; - /** * Responsible for storing a mapping of Fields that can be referenced from Visualforce to the type of the field. */ @@ -40,21 +38,23 @@ class ObjectFieldTypes { private static final String MDAPI_OBJECT_FILE_SUFFIX = ".object"; private static final String SFDX_FIELD_FILE_SUFFIX = ".field-meta.xml"; - private static final ImmutableMap STANDARD_FIELD_TYPES = - ImmutableMap.builder() - .put("createdbyid", ExpressionType.Lookup) - .put("createddate", ExpressionType.DateTime) - .put("id", ExpressionType.Lookup) - .put("isdeleted", ExpressionType.Checkbox) - .put("lastmodifiedbyid", ExpressionType.Lookup) - .put("lastmodifieddate", ExpressionType.DateTime) - .put("systemmodstamp", ExpressionType.DateTime) - .build(); + private static final Map STANDARD_FIELD_TYPES; + + static { + STANDARD_FIELD_TYPES = new ConcurrentHashMap<>(); + STANDARD_FIELD_TYPES.put("createdbyid", IdentifierType.Lookup); + STANDARD_FIELD_TYPES.put("createddate", IdentifierType.DateTime); + STANDARD_FIELD_TYPES.put("id", IdentifierType.Lookup); + STANDARD_FIELD_TYPES.put("isdeleted", IdentifierType.Checkbox); + STANDARD_FIELD_TYPES.put("lastmodifiedbyid", IdentifierType.Lookup); + STANDARD_FIELD_TYPES.put("lastmodifieddate", IdentifierType.DateTime); + STANDARD_FIELD_TYPES.put("systemmodstamp", IdentifierType.DateTime); + } /** * Cache of lowercase variable names to the variable type declared in the field's metadata file. */ - private final ConcurrentHashMap variableNameToVariableType; + private final ConcurrentHashMap variableNameToVariableType; /** * Keep track of which variables were already processed. Avoid processing if a page repeatedly asks for an entry @@ -78,8 +78,8 @@ class ObjectFieldTypes { ObjectFieldTypes() { this.variableNameToVariableType = new ConcurrentHashMap<>(); - this.variableNameProcessed = Sets.newConcurrentHashSet(); - this.objectFileProcessed = Sets.newConcurrentHashSet(); + this.variableNameProcessed = Collections.newSetFromMap(new ConcurrentHashMap()); + this.objectFileProcessed = Collections.newSetFromMap(new ConcurrentHashMap()); try { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); @@ -112,9 +112,9 @@ class ObjectFieldTypes { /** * Looks in {@code objectsDirectories} for a custom field identified by {@code expression}. * - * @return the ExpressionType for the field represented by {@code expression} or null the custom field isn't found. + * @return the IdentifierType for the field represented by {@code expression} or null the custom field isn't found. */ - public ExpressionType getVariableType(String expression, String vfFileName, List objectsDirectories) { + public IdentifierType getVariableType(String expression, String vfFileName, List objectsDirectories) { String lowerExpression = expression.toLowerCase(Locale.ROOT); if (variableNameToVariableType.containsKey(lowerExpression)) { @@ -201,10 +201,10 @@ class ObjectFieldTypes { Node fullNameNode = (Node) sfdxCustomFieldFullNameExpression.evaluate(document, XPathConstants.NODE); Node typeNode = (Node) sfdxCustomFieldTypeExpression.evaluate(document, XPathConstants.NODE); String type = typeNode.getNodeValue(); - ExpressionType expressionType = ExpressionType.fromString(type); + IdentifierType identifierType = IdentifierType.fromString(type); String key = customObjectName + "." + fullNameNode.getNodeValue(); - setVariableType(key, expressionType); + setVariableType(key, identifierType); } catch (Exception e) { throw new RuntimeException(e); } @@ -234,9 +234,9 @@ class ObjectFieldTypes { throw new RuntimeException("type evaluate failed for object=" + customObjectName + ", field=" + name + " " + fieldsNode.getTextContent()); } String type = typeNode.getNodeValue(); - ExpressionType expressionType = ExpressionType.fromString(type); + IdentifierType identifierType = IdentifierType.fromString(type); String key = customObjectName + "." + fullNameNode.getNodeValue(); - setVariableType(key, expressionType); + setVariableType(key, identifierType); } } } catch (Exception e) { @@ -251,7 +251,7 @@ class ObjectFieldTypes { * visualforce page. */ private void addStandardFields(String customObjectName) { - for (Map.Entry entry : STANDARD_FIELD_TYPES.entrySet()) { + for (Map.Entry entry : STANDARD_FIELD_TYPES.entrySet()) { setVariableType(customObjectName + "." + entry.getKey(), entry.getValue()); } } @@ -263,15 +263,15 @@ class ObjectFieldTypes { return str != null && str.toLowerCase(Locale.ROOT).endsWith(suffix.toLowerCase(Locale.ROOT)); } - private void setVariableType(String name, ExpressionType expressionType) { + private void setVariableType(String name, IdentifierType identifierType) { name = name.toLowerCase(Locale.ROOT); - ExpressionType previousType = variableNameToVariableType.put(name, expressionType); - if (previousType != null && !previousType.equals(expressionType)) { + IdentifierType previousType = variableNameToVariableType.put(name, identifierType); + if (previousType != null && !previousType.equals(identifierType)) { // It should not be possible ot have conflicting types for CustomFields throw new RuntimeException("Conflicting types for " + name + ". CurrentType=" - + expressionType + + identifierType + ", PreviousType=" + previousType); } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java index 8e03a20a8d..44289b6829 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java @@ -36,7 +36,7 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { private ObjectFieldTypes objectFieldTypes; private final String fileName; private String standardControllerName; - private final IdentityHashMap expressionTypes; + private final IdentityHashMap identifierTypes; /** * List of all Apex Class names that the VF page might refer to. These values come from either the @@ -53,11 +53,11 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { this.apexClassPropertyTypes = new ApexClassPropertyTypes(); this.objectFieldTypes = new ObjectFieldTypes(); this.apexClassNames = new ArrayList<>(); - this.expressionTypes = new IdentityHashMap<>(); + this.identifierTypes = new IdentityHashMap<>(); } - public IdentityHashMap getExpressionTypes() { - return this.expressionTypes; + public IdentityHashMap getIdentifierTypes() { + return this.identifierTypes; } /** @@ -98,7 +98,7 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { public Object visit(ASTElExpression node, Object data) { for (Map.Entry entry : getExpressionIdentifierNames(node).entrySet()) { String name = entry.getValue(); - ExpressionType type = null; + IdentifierType type = null; String[] parts = name.split("\\."); // Apex extensions take precedence over Standard controllers. @@ -139,7 +139,7 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { } if (type != null) { - expressionTypes.put(entry.getKey(), type); + identifierTypes.put(entry.getKey(), type); } else { LOGGER.fine("Unable to determine type for: " + name); } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java index 3c781ffc86..0d012576a1 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java @@ -11,7 +11,7 @@ import java.util.List; import java.util.Map; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.vf.ExpressionType; +import net.sourceforge.pmd.lang.vf.IdentifierType; import net.sourceforge.pmd.lang.vf.VfExpressionTypeVisitor; import net.sourceforge.pmd.lang.vf.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; @@ -21,7 +21,7 @@ import net.sourceforge.pmd.properties.PropertyFactory; /** * Represents a rule where the {@link net.sourceforge.pmd.lang.vf.ast.ASTIdentifier} nodes are enhanced with the - * node's {@link ExpressionType}. This is achieved by processing metadata files referenced by the Visualforce page. + * node's {@link IdentifierType}. This is achieved by processing metadata files referenced by the Visualforce page. */ abstract class AbstractVfTypedElExpressionRule extends AbstractVfRule { /** @@ -44,20 +44,20 @@ abstract class AbstractVfTypedElExpressionRule extends AbstractVfRule { .delim(',') .build(); - private Map expressionTypes; + private Map identifierTypes; AbstractVfTypedElExpressionRule() { definePropertyDescriptor(APEX_DIRECTORIES_DESCRIPTOR); definePropertyDescriptor(OBJECTS_DIRECTORIES_DESCRIPTOR); } - public ExpressionType getExpressionType(ASTIdentifier node) { - return expressionTypes.get(node); + public IdentifierType getIdentifierType(ASTIdentifier node) { + return identifierTypes.get(node); } @Override public void start(RuleContext ctx) { - this.expressionTypes = Collections.synchronizedMap(new IdentityHashMap()); + this.identifierTypes = Collections.synchronizedMap(new IdentityHashMap()); super.start(ctx); } @@ -80,7 +80,7 @@ abstract class AbstractVfTypedElExpressionRule extends AbstractVfRule { apexDirectories, objectsDirectories); visitor.visit(node, data); - this.expressionTypes.putAll(visitor.getExpressionTypes()); + this.identifierTypes.putAll(visitor.getIdentifierTypes()); } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java index 6891033ac5..a19edd024d 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java @@ -12,7 +12,7 @@ import java.util.Set; import java.util.regex.Pattern; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.vf.ExpressionType; +import net.sourceforge.pmd.lang.vf.IdentifierType; import net.sourceforge.pmd.lang.vf.ast.ASTArguments; import net.sourceforge.pmd.lang.vf.ast.ASTAttribute; import net.sourceforge.pmd.lang.vf.ast.ASTContent; @@ -411,8 +411,8 @@ public class VfUnescapeElRule extends AbstractVfTypedElExpressionRule { final List ids = expr.findChildrenOfType(ASTIdentifier.class); for (final ASTIdentifier id : ids) { - ExpressionType expressionType = getExpressionType(id); - if (expressionType != null && !expressionType.requiresEscaping) { + IdentifierType identifierType = getIdentifierType(id); + if (identifierType != null && !identifierType.requiresEscaping) { return false; } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java index 743b32c6e4..74765d17b1 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java @@ -25,28 +25,28 @@ public class ApexClassPropertyTypesTest { // must have the correct case since it is used to lookup the file. The Apex class name is guaranteed to be correct // in the Visualforce page, but the property names are not ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); - assertEquals(ExpressionType.Lookup, + assertEquals(IdentifierType.Lookup, apexClassPropertyTypes.getVariableType("ApexController.accOuntIdProp", vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); - assertEquals(ExpressionType.Lookup, + assertEquals(IdentifierType.Lookup, apexClassPropertyTypes.getVariableType("ApexController.AcCountId", vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); - assertEquals(ExpressionType.Text, + assertEquals(IdentifierType.Text, apexClassPropertyTypes.getVariableType("ApexController.AcCountname", vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); // InnerController assertEquals("The class should be parsed to Unknown. It's not a valid expression on its own.", - ExpressionType.Unknown, + IdentifierType.Unknown, apexClassPropertyTypes.getVariableType("ApexController.innErController", vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); - assertEquals(ExpressionType.Lookup, + assertEquals(IdentifierType.Lookup, apexClassPropertyTypes.getVariableType("ApexController.innErController.innErAccountIdProp", vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); - assertEquals(ExpressionType.Lookup, + assertEquals(IdentifierType.Lookup, apexClassPropertyTypes.getVariableType("ApexController.innErController.innErAccountid", vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); - assertEquals(ExpressionType.Text, + assertEquals(IdentifierType.Text, apexClassPropertyTypes.getVariableType("ApexController.innErController.innErAccountnAme", vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); @@ -61,7 +61,7 @@ public class ApexClassPropertyTypesTest { /** * It is possible to have a property and method with different types that resolve to the same Visualforce * expression. An example is an Apex class with a property "public String Foo {get; set;}" and a method of - * "Integer getFoo() { return 1; }". These properties should map to {@link ExpressionType#Unknown}. + * "Integer getFoo() { return 1; }". These properties should map to {@link IdentifierType#Unknown}. */ @Test public void testConflictingPropertyTypesMapsToUnknown() { @@ -69,7 +69,7 @@ public class ApexClassPropertyTypesTest { .toAbsolutePath(); String vfFileName = vfPagePath.toString(); ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); - assertEquals(ExpressionType.Unknown, + assertEquals(IdentifierType.Unknown, apexClassPropertyTypes.getVariableType("ApexWithConflictingPropertyTypes.ConflictingProp", vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ExpressionTypeTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ExpressionTypeTest.java deleted file mode 100644 index f80d7d717a..0000000000 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ExpressionTypeTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.vf; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import apex.jorje.semantic.symbol.type.BasicType; - -public class ExpressionTypeTest { - @Test - public void testFromString() { - assertEquals(ExpressionType.AutoNumber, ExpressionType.fromString("AutoNumber")); - assertEquals(ExpressionType.AutoNumber, ExpressionType.fromString("autonumber")); - assertEquals(ExpressionType.Unknown, ExpressionType.fromString("")); - assertEquals(ExpressionType.Unknown, ExpressionType.fromString(null)); - } - - @Test - public void testFromBasicType() { - assertEquals(ExpressionType.Checkbox, ExpressionType.fromBasicType(BasicType.BOOLEAN)); - assertEquals(ExpressionType.Number, ExpressionType.fromBasicType(BasicType.DECIMAL)); - assertEquals(ExpressionType.Number, ExpressionType.fromBasicType(BasicType.DOUBLE)); - assertEquals(ExpressionType.Unknown, ExpressionType.fromBasicType(BasicType.APEX_OBJECT)); - assertEquals(ExpressionType.Unknown, ExpressionType.fromBasicType(null)); - } - - @Test - public void testRequiresEncoding() { - assertFalse(ExpressionType.AutoNumber.requiresEscaping); - assertTrue(ExpressionType.Text.requiresEscaping); - } -} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/IdentifierTypeTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/IdentifierTypeTest.java new file mode 100644 index 0000000000..88d07113dc --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/IdentifierTypeTest.java @@ -0,0 +1,38 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import apex.jorje.semantic.symbol.type.BasicType; + +public class IdentifierTypeTest { + @Test + public void testFromString() { + assertEquals(IdentifierType.AutoNumber, IdentifierType.fromString("AutoNumber")); + assertEquals(IdentifierType.AutoNumber, IdentifierType.fromString("autonumber")); + assertEquals(IdentifierType.Unknown, IdentifierType.fromString("")); + assertEquals(IdentifierType.Unknown, IdentifierType.fromString(null)); + } + + @Test + public void testFromBasicType() { + assertEquals(IdentifierType.Checkbox, IdentifierType.fromBasicType(BasicType.BOOLEAN)); + assertEquals(IdentifierType.Number, IdentifierType.fromBasicType(BasicType.DECIMAL)); + assertEquals(IdentifierType.Number, IdentifierType.fromBasicType(BasicType.DOUBLE)); + assertEquals(IdentifierType.Unknown, IdentifierType.fromBasicType(BasicType.APEX_OBJECT)); + assertEquals(IdentifierType.Unknown, IdentifierType.fromBasicType(null)); + } + + @Test + public void testRequiresEncoding() { + assertFalse(IdentifierType.AutoNumber.requiresEscaping); + assertTrue(IdentifierType.Text.requiresEscaping); + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java index 0d4e308525..16ab824de9 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java @@ -79,17 +79,17 @@ public class ObjectFieldTypesTest { private void validateSfdxAccount(ObjectFieldTypes objectFieldTypes, Path vfPagePath, List paths) { String vfFileName = vfPagePath.toString(); - assertEquals(ExpressionType.Checkbox, + assertEquals(IdentifierType.Checkbox, objectFieldTypes.getVariableType("Account.Checkbox__c", vfFileName, paths)); - assertEquals(ExpressionType.DateTime, + assertEquals(IdentifierType.DateTime, objectFieldTypes.getVariableType("Account.DateTime__c", vfFileName, paths)); - assertEquals(ExpressionType.LongTextArea, + assertEquals(IdentifierType.LongTextArea, objectFieldTypes.getVariableType("Account.LongTextArea__c", vfFileName, paths)); - assertEquals(ExpressionType.Picklist, + assertEquals(IdentifierType.Picklist, objectFieldTypes.getVariableType("Account.Picklist__c", vfFileName, paths)); - assertEquals(ExpressionType.Text, + assertEquals(IdentifierType.Text, objectFieldTypes.getVariableType("Account.Text__c", vfFileName, paths)); - assertEquals(ExpressionType.TextArea, + assertEquals(IdentifierType.TextArea, objectFieldTypes.getVariableType("Account.TextArea__c", vfFileName, paths)); assertNull(objectFieldTypes.getVariableType("Account.DoesNotExist__c", vfFileName, paths)); } @@ -100,17 +100,17 @@ public class ObjectFieldTypesTest { private void validateMDAPIAccount(ObjectFieldTypes objectFieldTypes, Path vfPagePath, List paths) { String vfFileName = vfPagePath.toString(); - assertEquals(ExpressionType.Checkbox, + assertEquals(IdentifierType.Checkbox, objectFieldTypes.getVariableType("Account.MDCheckbox__c", vfFileName, paths)); - assertEquals(ExpressionType.DateTime, + assertEquals(IdentifierType.DateTime, objectFieldTypes.getVariableType("Account.MDDateTime__c", vfFileName, paths)); - assertEquals(ExpressionType.LongTextArea, + assertEquals(IdentifierType.LongTextArea, objectFieldTypes.getVariableType("Account.MDLongTextArea__c", vfFileName, paths)); - assertEquals(ExpressionType.Picklist, + assertEquals(IdentifierType.Picklist, objectFieldTypes.getVariableType("Account.MDPicklist__c", vfFileName, paths)); - assertEquals(ExpressionType.Text, + assertEquals(IdentifierType.Text, objectFieldTypes.getVariableType("Account.MDText__c", vfFileName, paths)); - assertEquals(ExpressionType.TextArea, + assertEquals(IdentifierType.TextArea, objectFieldTypes.getVariableType("Account.MDTextArea__c", vfFileName, paths)); assertNull(objectFieldTypes.getVariableType("Account.DoesNotExist__c", vfFileName, paths)); } From 6f7bebcecd5bf976f0ba5bc48a72775bfaf88068 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Wed, 28 Oct 2020 15:10:26 -0700 Subject: [PATCH 19/98] Configure visualforce to require Java 8 pmd-visualforce depends on pmd-apex. pmd-apex relies on Java 8. This change configures pmd-visualforce to also require Java 8. This is a breaking change that will need to be documented. --- docs/pages/pmd/devdocs/building.md | 2 +- pmd-dist/pom.xml | 10 +++++----- .../test/java/net/sourceforge/pmd/it/AllRulesIT.java | 4 ++-- .../net/sourceforge/pmd/it/BinaryDistributionIT.java | 6 +++--- pmd-visualforce/pom.xml | 4 ++++ pom.xml | 2 +- 6 files changed, 16 insertions(+), 12 deletions(-) diff --git a/docs/pages/pmd/devdocs/building.md b/docs/pages/pmd/devdocs/building.md index 0fcd7ce293..d9a7bcbc11 100644 --- a/docs/pages/pmd/devdocs/building.md +++ b/docs/pages/pmd/devdocs/building.md @@ -12,7 +12,7 @@ author: Tom Copeland, Xavier Le Vourch * JDK 11 or higher -{% include note.html content="While Java 11 is required for building, running PMD only requires Java 7 (or Java 8 for Apex and the Designer)." %} +{% include note.html content="While Java 11 is required for building, running PMD only requires Java 7 (or Java 8 for Apex, Scala, Visualforce, and the Designer)." %} You’ll need to either check out the source code or download the latest source release. Assuming you’ve got the latest source release, unzip it to a directory: diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index 7b055b0695..ed003e2940 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -148,11 +148,6 @@ pmd-jsp ${project.version} - - net.sourceforge.pmd - pmd-visualforce - ${project.version} - net.sourceforge.pmd pmd-kotlin @@ -264,6 +259,11 @@ pmd-ui ${pmd-designer.version} + + net.sourceforge.pmd + pmd-visualforce + ${project.version} + diff --git a/pmd-dist/src/test/java/net/sourceforge/pmd/it/AllRulesIT.java b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AllRulesIT.java index 91b96df299..8ba9e0533f 100644 --- a/pmd-dist/src/test/java/net/sourceforge/pmd/it/AllRulesIT.java +++ b/pmd-dist/src/test/java/net/sourceforge/pmd/it/AllRulesIT.java @@ -22,9 +22,9 @@ public class AllRulesIT extends AbstractBinaryDistributionTest { @Parameters public static Iterable languagesToTest() { if (PMDExecutor.isJava7Test()) { - // note: apex and scala require java8 + // note: apex, scala, and visualforce require java8 return Arrays.asList("java", "javascript", "jsp", "modelica", - "plsql", "pom", "visualforce", "velocitytemplate", "xml", "xsl"); + "plsql", "pom", "velocitytemplate", "xml", "xsl"); } // note: scala and wsdl have no rules return Arrays.asList("java", "apex", "javascript", "jsp", "modelica", 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 755e2085b1..d5bff3f134 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 @@ -25,10 +25,10 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest { private static final String SUPPORTED_LANGUAGES_PMD; static { - // note: apex and scala require java8 + // note: apex, visualforce, and scala require java8 if (PMDExecutor.isJava7Test()) { - SUPPORTED_LANGUAGES_CPD = "Supported languages: [cpp, cs, dart, ecmascript, fortran, go, groovy, java, jsp, kotlin, lua, matlab, modelica, objectivec, perl, php, plsql, python, ruby, swift, vf, xml]"; - SUPPORTED_LANGUAGES_PMD = "ecmascript, java, jsp, modelica, plsql, pom, vf, vm, wsdl, xml, xsl"; + SUPPORTED_LANGUAGES_CPD = "Supported languages: [cpp, cs, dart, ecmascript, fortran, go, groovy, java, jsp, kotlin, lua, matlab, modelica, objectivec, perl, php, plsql, python, ruby, swift, xml]"; + SUPPORTED_LANGUAGES_PMD = "ecmascript, java, jsp, modelica, plsql, pom, vm, wsdl, xml, xsl"; } else { SUPPORTED_LANGUAGES_CPD = "Supported languages: [apex, cpp, cs, dart, ecmascript, fortran, go, groovy, java, jsp, kotlin, lua, matlab, modelica, objectivec, perl, php, plsql, python, ruby, scala, swift, vf, xml]"; SUPPORTED_LANGUAGES_PMD = "apex, ecmascript, java, jsp, modelica, plsql, pom, scala, vf, vm, wsdl, xml, xsl"; diff --git a/pmd-visualforce/pom.xml b/pmd-visualforce/pom.xml index 2d8178b61d..af0d00675e 100644 --- a/pmd-visualforce/pom.xml +++ b/pmd-visualforce/pom.xml @@ -11,6 +11,10 @@ ../ + + 8 + + diff --git a/pom.xml b/pom.xml index 98cb9c7b86..1b1eaa0dc5 100644 --- a/pom.xml +++ b/pom.xml @@ -1081,7 +1081,6 @@ pmd-ruby pmd-swift pmd-test - pmd-visualforce pmd-vm pmd-xml @@ -1095,5 +1094,6 @@ pmd-scala-modules/pmd-scala-common pmd-scala-modules/pmd-scala_2.13 pmd-scala-modules/pmd-scala_2.12 + pmd-visualforce From ba2e91b3a0857d67e47ffce6b9e6c18200558330 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Thu, 29 Oct 2020 17:22:35 -0700 Subject: [PATCH 20/98] Exclude languages in AbstractRuleSetFactoryTest Allow subclasses of AbstractRuleSetFactoryTest to filter out languages that show up in the classpath but should not be tested. Change VFTestContstants to final instead of abstract. --- .../pmd/AbstractRuleSetFactoryTest.java | 18 ++++++++++++++++++ .../pmd/lang/vf/RuleSetFactoryTest.java | 7 ++----- .../pmd/lang/vf/VFTestContstants.java | 5 ++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java index bfa7f0a765..0c21329fea 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java @@ -17,6 +17,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -59,8 +60,22 @@ public abstract class AbstractRuleSetFactoryTest { private static SAXParser saxParser; protected Set validXPathClassNames = new HashSet<>(); + private Set languagesToSkip = new HashSet<>(); public AbstractRuleSetFactoryTest() { + this(null); + } + + /** + * Constructor used when a module that depends on another module wants to filter out the dependee's rulesets. + * + * @param languagesToSkip {@link Language}s that appear in the classpath via a dependency, but should be + * skipped because they aren't the primary language which the concrete instance of this class is testing. + */ + public AbstractRuleSetFactoryTest(Language... languagesToSkip) { + if (languagesToSkip != null) { + this.languagesToSkip.addAll(Arrays.asList(languagesToSkip)); + } validXPathClassNames.add(XPathRule.class.getName()); } @@ -263,6 +278,9 @@ public abstract class AbstractRuleSetFactoryTest { List result = new ArrayList<>(); for (Language language : LanguageRegistry.getLanguages()) { + if (this.languagesToSkip.contains(language)) { + continue; + } result.addAll(getRuleSetFileNames(language.getTerseName())); } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java index 9ae272d088..1e0e05f181 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java @@ -5,13 +5,10 @@ package net.sourceforge.pmd.lang.vf; import net.sourceforge.pmd.AbstractRuleSetFactoryTest; -import net.sourceforge.pmd.lang.apex.rule.ApexXPathRule; +import net.sourceforge.pmd.lang.apex.ApexLanguageModule; public class RuleSetFactoryTest extends AbstractRuleSetFactoryTest { public RuleSetFactoryTest() { - super(); - // Copied from net.sourceforge.pmd.lang.apex.RuleSetFactoryTest - // Apex rules are found in the classpath because this module has a dependency on pmd-apex - validXPathClassNames.add(ApexXPathRule.class.getName()); + super(new ApexLanguageModule()); } } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestContstants.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestContstants.java index 5469f26389..85839e1b9d 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestContstants.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestContstants.java @@ -10,7 +10,10 @@ import java.nio.file.Paths; import java.util.Collections; import java.util.List; -public abstract class VFTestContstants { +public final class VFTestContstants { + private VFTestContstants() { + } + private static final Path ROOT_PATH = Paths.get("src", "test", "resources", "net", "sourceforge", "pmd", "lang", "vf").toAbsolutePath(); From 1ff1e07c4dd10d61141a54138f932ab129b495f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 14:43:38 +0100 Subject: [PATCH 21/98] Return a report instead of side-effecting on it I think this will be more compatible with pmd 7. --- .../main/java/net/sourceforge/pmd/PMD.java | 37 ++++++------------- .../pmd/ant/internal/PMDTaskImpl.java | 3 +- 2 files changed, 13 insertions(+), 27 deletions(-) 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 370c0ad672..660148eea4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -18,7 +18,6 @@ import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; @@ -44,7 +43,6 @@ import net.sourceforge.pmd.processor.AbstractPMDProcessor; import net.sourceforge.pmd.processor.MonoThreadProcessor; import net.sourceforge.pmd.processor.MultiThreadProcessor; import net.sourceforge.pmd.renderers.Renderer; -import net.sourceforge.pmd.stat.Metric; import net.sourceforge.pmd.util.ClasspathClassLoader; import net.sourceforge.pmd.util.FileUtil; import net.sourceforge.pmd.util.IOUtil; @@ -240,28 +238,15 @@ public class PMD { renderer.start(); } - final AtomicInteger violations = new AtomicInteger(0); - Report report = new Report(); - report.addListener(new ThreadSafeReportListener() { - @Override - public void ruleViolationAdded(RuleViolation ruleViolation) { - violations.getAndIncrement(); - } - - @Override - public void metricAdded(Metric metric) { - // ignored - not needed for counting violations - } - }); - + Report report; try (TimedOperation to = TimeTracker.startOperation(TimedOperationCategory.FILE_PROCESSING)) { - processFiles(configuration, Arrays.asList(ruleSets.getAllRuleSets()), files, report, renderers); + report = processFiles(configuration, Arrays.asList(ruleSets.getAllRuleSets()), files, renderers); } try (TimedOperation rto = TimeTracker.startOperation(TimedOperationCategory.REPORTING)) { renderer.end(); renderer.flush(); - return violations.get(); + return report.getViolations().size(); } } catch (Exception e) { String message = e.getMessage(); @@ -320,7 +305,7 @@ public class PMD { * @param renderers * List of {@link Renderer}s * - * @deprecated Use {@link #processFiles(PMDConfiguration, List, Collection, Report, List)} + * @deprecated Use {@link #processFiles(PMDConfiguration, List, Collection, List)} * so as not to depend on {@link RuleSetFactory}. Note that this sorts the list of data sources in-place, * which won't be fixed */ @@ -349,16 +334,17 @@ public class PMD { * as parameters * @param rulesets Parsed rulesets * @param files Files to process - * @param report Report in which violations are accumulated * @param renderers Renderers that render the report * + * @return Report in which violations are accumulated + * * @throws RuntimeException If processing fails */ - public static void processFiles(final PMDConfiguration configuration, - final List rulesets, - final Collection files, - final Report report, - final List renderers) { + public static Report processFiles(final PMDConfiguration configuration, + final List rulesets, + final Collection files, + final List renderers) { + Report report = new Report(); encourageToUseIncrementalAnalysis(configuration); report.addListener(configuration.getAnalysisCache()); @@ -369,6 +355,7 @@ public class PMD { ctx.setReport(report); newFileProcessor(configuration).processFiles(new RuleSets(rulesets), sortedFiles, ctx, renderers); configuration.getAnalysisCache().persist(); + return report; } private static void sortFiles(final PMDConfiguration configuration, final List files) { 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 101bd4d8ec..c804f5700f 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 @@ -131,7 +131,6 @@ public class PMDTaskImpl { // TODO Do we really need all this in a loop over each FileSet? Seems // like a lot of redundancy - Report report = new Report(); Report errorReport = new Report(); final AtomicInteger reportSize = new AtomicInteger(); final String separator = System.getProperty("file.separator"); @@ -190,7 +189,7 @@ public class PMDTaskImpl { renderers.add(renderer); } try { - PMD.processFiles(configuration, rulesetList, files, report, renderers); + PMD.processFiles(configuration, rulesetList, files, renderers); } catch (ContextedRuntimeException e) { if (e.getFirstContextValue("filename") instanceof String) { handleError((String) e.getFirstContextValue("filename"), errorReport, e); From 08fca9367e3722fc65b7c7911fe4ade978158c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 14:44:54 +0100 Subject: [PATCH 22/98] Rename RuleSetParser to RuleSetLoader --- .../pmd/lang/apex/DefaultRulesetTest.java | 4 ++-- .../main/java/net/sourceforge/pmd/PMD.java | 2 +- .../net/sourceforge/pmd/RuleSetFactory.java | 24 +++++++++---------- .../pmd/RuleSetFactoryCompatibility.java | 2 +- ...{RuleSetParser.java => RuleSetLoader.java} | 20 ++++++++-------- .../net/sourceforge/pmd/RuleSetReference.java | 2 +- .../sourceforge/pmd/RuleSetReferenceId.java | 2 +- .../sourceforge/pmd/RulesetsFactoryUtils.java | 16 ++++++------- .../pmd/ant/internal/PMDTaskImpl.java | 6 ++--- .../sourceforge/pmd/RuleSetFactoryTest.java | 16 ++++++------- .../pmd/lang/java/PMD5RulesetTest.java | 4 ++-- .../pmd/lang/java/QuickstartRulesetTest.java | 4 ++-- 12 files changed, 51 insertions(+), 51 deletions(-) rename pmd-core/src/main/java/net/sourceforge/pmd/{RuleSetParser.java => RuleSetLoader.java} (88%) diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java index 8a1c2db3c1..e8f53a6d68 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/DefaultRulesetTest.java @@ -16,13 +16,13 @@ import org.junit.contrib.java.lang.system.SystemErrRule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetParser; +import net.sourceforge.pmd.RuleSetLoader; public class DefaultRulesetTest { @Rule public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests(); - private RuleSetFactory factory = new RuleSetParser().enableCompatibility(false).toFactory(); + private RuleSetFactory factory = new RuleSetLoader().enableCompatibility(false).toFactory(); @Test public void loadDefaultRuleset() throws Exception { 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 660148eea4..118904dbcc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -220,7 +220,7 @@ public class PMD { public static int doPMD(PMDConfiguration configuration) { // Load the RuleSets - final RuleSetFactory ruleSetFactory = RuleSetParser.fromPmdConfig(configuration).toFactory(); + final RuleSetFactory ruleSetFactory = RuleSetLoader.fromPmdConfig(configuration).toFactory(); final RuleSets ruleSets = RulesetsFactoryUtils.getRuleSetsWithBenchmark(configuration.getRuleSets(), ruleSetFactory); if (ruleSets == null) { return PMDCommandLineInterface.NO_ERRORS_STATUS; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 0778035fc3..093a5c12d9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -43,11 +43,11 @@ import net.sourceforge.pmd.util.ResourceLoader; /** * RuleSetFactory is responsible for creating RuleSet instances from XML - * content. See {@link RuleSetParser} for configuration options and + * content. See {@link RuleSetLoader} for configuration options and * their defaults. * - * @deprecated Use a {@link RuleSetParser} instead. This will be hidden in PMD 7 - * (it's the implementation, while {@link RuleSetParser} is the API). + * @deprecated Use a {@link RuleSetLoader} instead. This will be hidden in PMD 7 + * (it's the implementation, while {@link RuleSetLoader} is the API). */ @Deprecated public class RuleSetFactory { @@ -67,7 +67,7 @@ public class RuleSetFactory { private final Map parsedRulesets = new HashMap<>(); /** - * @deprecated Use a {@link RuleSetParser} to build a new factory + * @deprecated Use a {@link RuleSetLoader} to build a new factory */ @Deprecated // to be removed with PMD 7.0.0. public RuleSetFactory() { @@ -75,7 +75,7 @@ public class RuleSetFactory { } /** - * @deprecated Use a {@link RuleSetParser} to build a new factory + * @deprecated Use a {@link RuleSetLoader} to build a new factory */ @Deprecated // to be removed with PMD 7.0.0. public RuleSetFactory(final ClassLoader classLoader, final RulePriority minimumPriority, @@ -84,7 +84,7 @@ public class RuleSetFactory { } /** - * @deprecated Use a {@link RuleSetParser} to build a new factory + * @deprecated Use a {@link RuleSetLoader} to build a new factory */ @Deprecated // to be hidden with PMD 7.0.0. public RuleSetFactory(final ResourceLoader resourceLoader, final RulePriority minimumPriority, @@ -123,7 +123,7 @@ public class RuleSetFactory { } - RuleSetFactory(RuleSetParser config) { + RuleSetFactory(RuleSetLoader config) { this(config.resourceLoader, config.minimumPriority, config.warnDeprecated, config.enableCompatibility, config.includeDeprecatedRuleReferences); } @@ -184,7 +184,7 @@ public class RuleSetFactory { * @throws RuleSetNotFoundException * if unable to find a resource. * - * @deprecated Use {@link RuleSetParser#parseFromResource(String)}, + * @deprecated Use {@link RuleSetLoader#parseFromResource(String)}, * but note that that method does not split on commas */ @Deprecated @@ -227,7 +227,7 @@ public class RuleSetFactory { * @throws RuleSetNotFoundException * if unable to find a resource. * - * @deprecated Use {@link RuleSetParser#parseFromResource(String)} and discard the rest of the list. + * @deprecated Use {@link RuleSetLoader#parseFromResource(String)} and discard the rest of the list. */ @Deprecated public RuleSet createRuleSet(String referenceString) throws RuleSetNotFoundException { @@ -846,11 +846,11 @@ public class RuleSetFactory { /** - * Create a new {@link RuleSetParser} with the same config as this + * Create a new {@link RuleSetLoader} with the same config as this * factory. This is a transitional API. */ - public RuleSetParser toParser() { - return new RuleSetParser().loadResourcesWith(resourceLoader) + public RuleSetLoader toParser() { + return new RuleSetLoader().loadResourcesWith(resourceLoader) .filterAbovePriority(minimumPriority) .warnDeprecated(warnDeprecated) .enableCompatibility(compatibilityFilter != null) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java index b3d5d51a7b..6ed1d189a4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java @@ -27,7 +27,7 @@ import net.sourceforge.pmd.annotation.InternalApi; * * @see issue 1360 * - * @deprecated Use {@link RuleSetParser#enableCompatibility(boolean)} to enable this feature. + * @deprecated Use {@link RuleSetLoader#enableCompatibility(boolean)} to enable this feature. * This implementation is internal API. */ @InternalApi diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java similarity index 88% rename from pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java rename to pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java index 72b3dc60b7..cb6cb1cdc5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetParser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -17,9 +17,9 @@ import net.sourceforge.pmd.util.ResourceLoader; * API, see eg {@link #warnDeprecated(boolean)}. To create a list of * rulesets, use {@link #parseFromResource(String)}. */ -public final class RuleSetParser { +public final class RuleSetLoader { - ResourceLoader resourceLoader = new ResourceLoader(RuleSetParser.class.getClassLoader()); + ResourceLoader resourceLoader = new ResourceLoader(RuleSetLoader.class.getClassLoader()); RulePriority minimumPriority = RulePriority.LOW; boolean warnDeprecated = true; boolean enableCompatibility = true; @@ -30,13 +30,13 @@ public final class RuleSetParser { * paths to external ruleset references. The default uses PMD's * own classpath. */ - public RuleSetParser loadResourcesWith(ClassLoader classLoader) { + public RuleSetLoader loadResourcesWith(ClassLoader classLoader) { this.resourceLoader = new ResourceLoader(classLoader); return this; } // internal - RuleSetParser loadResourcesWith(ResourceLoader loader) { + RuleSetLoader loadResourcesWith(ResourceLoader loader) { this.resourceLoader = loader; return this; } @@ -47,7 +47,7 @@ public final class RuleSetParser { * ie, no filtering occurs. * @return This instance, modified */ - public RuleSetParser filterAbovePriority(RulePriority minimumPriority) { + public RuleSetLoader filterAbovePriority(RulePriority minimumPriority) { this.minimumPriority = minimumPriority; return this; } @@ -57,7 +57,7 @@ public final class RuleSetParser { * This is enabled by default. * @return This instance, modified */ - public RuleSetParser warnDeprecated(boolean warn) { + public RuleSetLoader warnDeprecated(boolean warn) { this.warnDeprecated = warn; return this; } @@ -69,7 +69,7 @@ public final class RuleSetParser { * error. * @return This instance, modified */ - public RuleSetParser enableCompatibility(boolean enable) { + public RuleSetLoader enableCompatibility(boolean enable) { this.enableCompatibility = enable; return this; } @@ -81,7 +81,7 @@ public final class RuleSetParser { * * @return This instance, modified */ - public RuleSetParser includeDeprecatedRuleReferences(boolean enable) { + public RuleSetLoader includeDeprecatedRuleReferences(boolean enable) { this.includeDeprecatedRuleReferences = enable; return this; } @@ -149,8 +149,8 @@ public final class RuleSetParser { * Configure a new ruleset factory builder according to the parameters * of the given PMD configuration. */ - public static RuleSetParser fromPmdConfig(PMDConfiguration configuration) { - return new RuleSetParser().filterAbovePriority(configuration.getMinimumPriority()) + public static RuleSetLoader fromPmdConfig(PMDConfiguration configuration) { + return new RuleSetLoader().filterAbovePriority(configuration.getMinimumPriority()) .enableCompatibility(configuration.isRuleSetFactoryCompatibilityEnabled()); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReference.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReference.java index 32e2eeedb7..cdc6e5f77c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReference.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReference.java @@ -13,7 +13,7 @@ import net.sourceforge.pmd.annotation.InternalApi; /** * This class represents a reference to RuleSet. * - * @deprecated This is part of the internals of the {@link RuleSetParser}. + * @deprecated This is part of the internals of the {@link RuleSetLoader}. */ @Deprecated @InternalApi diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java index bac49f5f86..66dd5e47c9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java @@ -80,7 +80,7 @@ import net.sourceforge.pmd.util.ResourceLoader; * * * - * @deprecated This is part of the internals of the {@link RuleSetParser}. + * @deprecated This is part of the internals of the {@link RuleSetLoader}. */ @Deprecated @InternalApi diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java index c835b9ac9e..135736f8a6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java @@ -14,7 +14,7 @@ import net.sourceforge.pmd.benchmark.TimedOperationCategory; import net.sourceforge.pmd.util.ResourceLoader; /** - * @deprecated Use a {@link RuleSetParser} instead + * @deprecated Use a {@link RuleSetLoader} instead */ @Deprecated public final class RulesetsFactoryUtils { @@ -80,7 +80,7 @@ public final class RulesetsFactoryUtils { } /** - * @deprecated Use a {@link RuleSetParser} + * @deprecated Use a {@link RuleSetLoader} */ @InternalApi @Deprecated @@ -101,7 +101,7 @@ public final class RulesetsFactoryUtils { * * @see #createFactory(PMDConfiguration, ClassLoader) * - * @deprecated Use {@link RuleSetParser#fromPmdConfig(PMDConfiguration)} + * @deprecated Use {@link RuleSetLoader#fromPmdConfig(PMDConfiguration)} */ @Deprecated public static RuleSetFactory createFactory(final PMDConfiguration configuration) { @@ -114,7 +114,7 @@ public final class RulesetsFactoryUtils { * * @return A ruleset factory * - * @see RuleSetParser + * @see RuleSetLoader */ public static RuleSetFactory defaultFactory() { return new RuleSetFactory(); @@ -132,7 +132,7 @@ public final class RulesetsFactoryUtils { * * @see #createFactory(PMDConfiguration) * - * @deprecated Use a {@link RuleSetParser} + * @deprecated Use a {@link RuleSetLoader} */ @Deprecated public static RuleSetFactory createFactory(final PMDConfiguration configuration, ClassLoader classLoader) { @@ -156,7 +156,7 @@ public final class RulesetsFactoryUtils { * * @see #createFactory(PMDConfiguration) * - * @deprecated Use a {@link RuleSetParser} + * @deprecated Use a {@link RuleSetLoader} */ @Deprecated public static RuleSetFactory createFactory(ClassLoader classLoader, @@ -180,7 +180,7 @@ public final class RulesetsFactoryUtils { * * @see #createFactory(PMDConfiguration) * - * @deprecated Use a {@link RuleSetParser} + * @deprecated Use a {@link RuleSetLoader} */ @Deprecated public static RuleSetFactory createFactory(RulePriority minimumPriority, @@ -205,7 +205,7 @@ public final class RulesetsFactoryUtils { * @return A ruleset factory * * @see #createFactory(PMDConfiguration) - * @deprecated Use a {@link RuleSetParser} + * @deprecated Use a {@link RuleSetLoader} */ @Deprecated public static RuleSetFactory createFactory(RulePriority minimumPriority, 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 c804f5700f..685c459bd9 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 @@ -29,7 +29,7 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetParser; +import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.ant.Formatter; @@ -102,7 +102,7 @@ public class PMDTaskImpl { setupClassLoader(); // Setup RuleSetFactory and validate RuleSets - RuleSetParser rulesetParser = RuleSetParser.fromPmdConfig(configuration) + RuleSetLoader rulesetLoader = RuleSetLoader.fromPmdConfig(configuration) .loadResourcesWith(setupResourceLoader()); // This is just used to validate and display rules. Each thread will create its own ruleset @@ -112,7 +112,7 @@ public class PMDTaskImpl { configuration.setRuleSets(project.replaceProperties(ruleSetString)); } - final RuleSets ruleSets = RulesetsFactoryUtils.getRuleSets(configuration.getRuleSets(), rulesetParser.toFactory()); + final RuleSets ruleSets = RulesetsFactoryUtils.getRuleSets(configuration.getRuleSets(), rulesetLoader.toFactory()); List rulesetList = Arrays.asList(ruleSets.getAllRuleSets()); logRulesUsed(ruleSets); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index 455d0287b9..bfbc7ce547 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -505,7 +505,7 @@ public class RuleSetFactoryTest { @Test public void testReferencePriority() throws RuleSetNotFoundException { - RuleSetParser config = new RuleSetParser().warnDeprecated(false).enableCompatibility(true); + RuleSetLoader config = new RuleSetLoader().warnDeprecated(false).enableCompatibility(true); RuleSetFactory rsf = config.filterAbovePriority(RulePriority.LOW).toFactory(); RuleSet ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); @@ -546,7 +546,7 @@ public class RuleSetFactoryTest { @Test public void testOverridePriorityLoadWithMinimum() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetParser().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true).toFactory(); + RuleSetFactory rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true).toFactory(); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); // only one rule should remain, since we filter out the other rule by minimum priority assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -567,13 +567,13 @@ public class RuleSetFactoryTest { @Test public void testExcludeWithMinimumPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetParser().filterAbovePriority(RulePriority.HIGH).toFactory(); + RuleSetFactory rsf = new RuleSetLoader().filterAbovePriority(RulePriority.HIGH).toFactory(); RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // no rules should be loaded assertEquals("Number of Rules", 0, ruleset.getRules().size()); // now, load with default minimum priority - rsf = new RuleSetParser().filterAbovePriority(RulePriority.LOW).toFactory(); + rsf = new RuleSetLoader().filterAbovePriority(RulePriority.LOW).toFactory(); ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // only one rule, we have excluded one... assertEquals("Number of Rules", 1, ruleset.getRules().size()); @@ -602,9 +602,9 @@ public class RuleSetFactoryTest { @Test public void testSetPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetParser().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false).toFactory(); + RuleSetFactory rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false).toFactory(); assertEquals(0, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); - rsf = new RuleSetParser().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false).toFactory(); + rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false).toFactory(); assertEquals(1, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); } @@ -779,7 +779,7 @@ public class RuleSetFactoryTest { + " Ruleset which references a empty ruleset\n" + "\n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetParser().loadResourcesWith(new ResourceLoader()).filterAbovePriority(RulePriority.LOW).warnDeprecated(true).enableCompatibility(true).toFactory(); + RuleSetFactory ruleSetFactory = new RuleSetLoader().loadResourcesWith(new ResourceLoader()).filterAbovePriority(RulePriority.LOW).warnDeprecated(true).enableCompatibility(true).toFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet(ref); assertEquals(0, ruleset.getRules().size()); @@ -1284,7 +1284,7 @@ public class RuleSetFactoryTest { } private RuleSet loadRuleSetWithDeprecationWarnings(String ruleSetXml) throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetParser().warnDeprecated(true).enableCompatibility(false).toFactory(); + RuleSetFactory rsf = new RuleSetLoader().warnDeprecated(true).enableCompatibility(false).toFactory(); return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml)); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java index d4406a6ed0..23de5ce3de 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/PMD5RulesetTest.java @@ -9,13 +9,13 @@ import org.junit.Test; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetParser; +import net.sourceforge.pmd.RuleSetLoader; public class PMD5RulesetTest { @Test public void loadRuleset() throws Exception { - RuleSetFactory ruleSetFactory = new RuleSetParser().toFactory(); + RuleSetFactory ruleSetFactory = new RuleSetLoader().toFactory(); RuleSet ruleset = ruleSetFactory.createRuleSet("net/sourceforge/pmd/lang/java/pmd5ruleset.xml"); Assert.assertNotNull(ruleset); Assert.assertNull(ruleset.getRuleByName("GuardLogStatementJavaUtil")); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java index 270238f2a8..16c3142ab9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/QuickstartRulesetTest.java @@ -16,8 +16,8 @@ import org.junit.contrib.java.lang.system.SystemErrRule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetFactory; +import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.RuleSetParser; public class QuickstartRulesetTest { @@ -49,7 +49,7 @@ public class QuickstartRulesetTest { } }); - RuleSetFactory ruleSetFactory = new RuleSetParser().enableCompatibility(false).toFactory(); + RuleSetFactory ruleSetFactory = new RuleSetLoader().enableCompatibility(false).toFactory(); RuleSet quickstart = ruleSetFactory.createRuleSet("rulesets/java/quickstart.xml"); Assert.assertFalse(quickstart.getRules().isEmpty()); } From 4f175a1c4fca8a88d9a688f68b149ec2fbcb6142 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 14:48:29 +0100 Subject: [PATCH 23/98] Hide RuleSetLoader fields --- .../net/sourceforge/pmd/RuleSetFactory.java | 4 ---- .../net/sourceforge/pmd/RuleSetLoader.java | 23 +++++++++++++------ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 093a5c12d9..aa490e4dea 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -123,10 +123,6 @@ public class RuleSetFactory { } - RuleSetFactory(RuleSetLoader config) { - this(config.resourceLoader, config.minimumPriority, config.warnDeprecated, config.enableCompatibility, config.includeDeprecatedRuleReferences); - } - /** * Gets the compatibility filter in order to adjust it, e.g. add additional * filters. 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 cb6cb1cdc5..27910274cd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -19,11 +19,11 @@ import net.sourceforge.pmd.util.ResourceLoader; */ public final class RuleSetLoader { - ResourceLoader resourceLoader = new ResourceLoader(RuleSetLoader.class.getClassLoader()); - RulePriority minimumPriority = RulePriority.LOW; - boolean warnDeprecated = true; - boolean enableCompatibility = true; - boolean includeDeprecatedRuleReferences = false; + private ResourceLoader resourceLoader = new ResourceLoader(RuleSetLoader.class.getClassLoader()); + private RulePriority minimumPriority = RulePriority.LOW; + private boolean warnDeprecated = true; + private boolean enableCompatibility = true; + private boolean includeDeprecatedRuleReferences = false; /** * Specify that the given classloader should be used to resolve @@ -89,10 +89,19 @@ public final class RuleSetLoader { /** * Create a new rule set factory, if you have to (that class is deprecated). * That factory will use the configuration that was set using the setters of this. + * + * @deprecated {@link RuleSetFactory} is deprecated, replace its usages with usages of this class, + * or of static factory methods of {@link RuleSet} */ @Deprecated public RuleSetFactory toFactory() { - return new RuleSetFactory(this); + return new RuleSetFactory( + this.resourceLoader, + this.minimumPriority, + this.warnDeprecated, + this.enableCompatibility, + this.includeDeprecatedRuleReferences + ); } @@ -141,7 +150,7 @@ public final class RuleSetLoader { // package private RuleSet parseFromResource(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { - return new RuleSetFactory(this).createRuleSet(ruleSetReferenceId); + return toFactory().createRuleSet(ruleSetReferenceId); } From 8f7801c2e71b62e9520f097aec7ed69648cda64a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 14:51:23 +0100 Subject: [PATCH 24/98] Remove try with resources --- pmd-core/src/main/java/net/sourceforge/pmd/PMD.java | 2 +- .../pmd/processor/AbstractPMDProcessor.java | 12 ++---------- 2 files changed, 3 insertions(+), 11 deletions(-) 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 118904dbcc..8985dfa8a7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -344,8 +344,8 @@ public class PMD { final List rulesets, final Collection files, final List renderers) { - Report report = new Report(); encourageToUseIncrementalAnalysis(configuration); + Report report = new Report(); report.addListener(configuration.getAnalysisCache()); List sortedFiles = new ArrayList<>(files); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java index d0ad3ae1ef..a77181b2e3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java @@ -118,16 +118,8 @@ public abstract class AbstractPMDProcessor { @Deprecated public void processFiles(RuleSetFactory ruleSetFactory, List files, RuleContext ctx, List renderers) { - try { - final RuleSets rs = createRuleSets(ruleSetFactory, ctx.getReport()); - processFiles(rs, files, ctx, renderers); - } finally { - // in case we analyzed files within Zip Files/Jars, we need to close them after - // the analysis is finished - for (DataSource dataSource : files) { - IOUtils.closeQuietly(dataSource); - } - } + RuleSets rs = createRuleSets(ruleSetFactory, ctx.getReport()); + processFiles(rs, files, ctx, renderers); } @SuppressWarnings("PMD.CloseResource") From e30fcaf31dffa9fa3c2e15df4f901bce1e212460 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 14:52:45 +0100 Subject: [PATCH 25/98] Rename to loadFromResources --- .../java/net/sourceforge/pmd/RuleSetFactory.java | 4 ++-- .../java/net/sourceforge/pmd/RuleSetLoader.java | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index aa490e4dea..199e36a25d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -180,7 +180,7 @@ public class RuleSetFactory { * @throws RuleSetNotFoundException * if unable to find a resource. * - * @deprecated Use {@link RuleSetLoader#parseFromResource(String)}, + * @deprecated Use {@link RuleSetLoader#loadFromResource(String)}, * but note that that method does not split on commas */ @Deprecated @@ -223,7 +223,7 @@ public class RuleSetFactory { * @throws RuleSetNotFoundException * if unable to find a resource. * - * @deprecated Use {@link RuleSetLoader#parseFromResource(String)} and discard the rest of the list. + * @deprecated Use {@link RuleSetLoader#loadFromResource(String)} and discard the rest of the list. */ @Deprecated public RuleSet createRuleSet(String referenceString) throws RuleSetNotFoundException { 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 27910274cd..34a876717b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -15,7 +15,7 @@ import net.sourceforge.pmd.util.ResourceLoader; * Configurable ruleset parser. Note that this replaces the API of {@link RulesetsFactoryUtils} * and {@link RuleSetFactory}. This can be configured using a fluent * API, see eg {@link #warnDeprecated(boolean)}. To create a list of - * rulesets, use {@link #parseFromResource(String)}. + * rulesets, use {@link #loadFromResource(String)}. */ public final class RuleSetLoader { @@ -116,8 +116,8 @@ public final class RuleSetLoader { * * @throws RuleSetNotFoundException If the path does not correspond to a resource */ - public RuleSet parseFromResource(String rulesetPath) throws RuleSetNotFoundException { - return parseFromResource(new RuleSetReferenceId(rulesetPath)); + public RuleSet loadFromResource(String rulesetPath) throws RuleSetNotFoundException { + return loadFromResource(new RuleSetReferenceId(rulesetPath)); } /** @@ -128,10 +128,10 @@ public final class RuleSetLoader { * @throws RuleSetNotFoundException If any resource throws * @throws NullPointerException If the parameter, or any component is null */ - public List parseFromResources(Collection paths) throws RuleSetNotFoundException { + public List loadFromResources(Collection paths) throws RuleSetNotFoundException { List ruleSets = new ArrayList<>(paths.size()); for (String path : paths) { - ruleSets.add(parseFromResource(path)); + ruleSets.add(loadFromResource(path)); } return ruleSets; } @@ -144,12 +144,12 @@ public final class RuleSetLoader { * @throws RuleSetNotFoundException If any resource throws * @throws NullPointerException If the parameter, or any component is null */ - public List parseFromResources(String... paths) throws RuleSetNotFoundException { - return parseFromResources(Arrays.asList(paths)); + public List loadFromResources(String... paths) throws RuleSetNotFoundException { + return loadFromResources(Arrays.asList(paths)); } // package private - RuleSet parseFromResource(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { + RuleSet loadFromResource(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { return toFactory().createRuleSet(ruleSetReferenceId); } From ccaf57e8d650f4564ff65f9f4ec55ee800a1b273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 14:54:54 +0100 Subject: [PATCH 26/98] Update release notes --- docs/pages/release_notes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 710428b79d..4487ee4bf5 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -22,14 +22,14 @@ This is a {{ site.pmd.release_type }} release. ##### Around RuleSet parsing -* {% jdoc core::RuleSetFactory %} and {% jdoc core::RuleSetFactoryUtils %} have been deprecated in favor of {% jdoc core::RuleSetParser %}. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. +* {% jdoc core::RuleSetFactory %} and {% jdoc core::RuleSetFactoryUtils %} have been deprecated in favor of {% jdoc core::RuleSetLoader %}. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. * Some static creation methods have been added to {% jdoc core::RuleSet %} for simple cases, eg {% jdoc core::RuleSet#forSingleRule(core::Rule) %}. These replace some counterparts in {% jdoc core::RuleSetFactory %} * Since {% jdoc core::RuleSets %} is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. * {% jdoc core::RuleSetReferenceId %}, {% jdoc core::RuleSetReference %}, {% jdoc core::RuleSetFactoryCompatibility %} are deprecated. They are most likely not relevant outside of the implementation of pmd-core. ##### Around the `PMD` class -Many APIs around PMD's entry point ({% jdoc core::PMD %}) have been deprecated, including: +Many classes around PMD's entry point ({% jdoc core::PMD %}) have been deprecated as internal, including: * The contents of the packages {% jdoc_package core::cli %}, {% jdoc_package core::processor %} * {% jdoc core::SourceCodeProcessor %} * The constructors of {% jdoc core::PMD %} (the class will be made a utility class) From 6eee3d037f4af2b6c591f361d0e91e337fcd7742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 15:05:58 +0100 Subject: [PATCH 27/98] Provide replacement api for getRegisteredRuleSets --- .../main/java/net/sourceforge/pmd/PMD.java | 2 +- .../net/sourceforge/pmd/RuleSetFactory.java | 42 ++++--------------- .../net/sourceforge/pmd/RuleSetLoader.java | 42 +++++++++++++++++++ .../pmd/RuleSetNotFoundException.java | 4 ++ .../java/net/sourceforge/pmd/RuleSets.java | 5 +++ 5 files changed, 61 insertions(+), 34 deletions(-) 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 8985dfa8a7..47619f4844 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -321,7 +321,7 @@ public class PMD { // Make sure the cache is listening for analysis results ctx.getReport().addListener(configuration.getAnalysisCache()); - final RuleSetFactory silentFactory = ruleSetFactory.toParser().warnDeprecated(false).toFactory(); + final RuleSetFactory silentFactory = ruleSetFactory.toLoader().warnDeprecated(false).toFactory(); newFileProcessor(configuration).processFiles(silentFactory, files, ctx, renderers); configuration.getAnalysisCache().persist(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 199e36a25d..8cf19d28d5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -13,7 +13,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Properties; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -34,8 +33,6 @@ import org.xml.sax.InputSource; import org.xml.sax.SAXException; import net.sourceforge.pmd.RuleSet.RuleSetBuilder; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.MockRule; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.rules.RuleFactory; @@ -109,13 +106,11 @@ public class RuleSetFactory { /** * Constructor copying all configuration from another factory. * - * @param factory - * The factory whose configuration to copy. - * @param warnDeprecated - * Whether deprecation warnings are to be produced by this - * factory + * @param factory The factory whose configuration to copy. + * @param warnDeprecated Whether deprecation warnings are to be produced by this + * factory * - * @deprecated Use {@link #toParser()} to rebuild a factory from a configuration + * @deprecated Use {@link #toLoader()} to rebuild a factory from a configuration */ @Deprecated public RuleSetFactory(final RuleSetFactory factory, final boolean warnDeprecated) { @@ -141,30 +136,11 @@ public class RuleSetFactory { * * @throws RuleSetNotFoundException if the ruleset file could not be found * - * @deprecated This is apparently only used in code deprecated for removal + * @deprecated Use {@link RuleSetLoader#getStandardRuleSets()} */ @Deprecated public Iterator getRegisteredRuleSets() throws RuleSetNotFoundException { - String rulesetsProperties = null; - List ruleSetReferenceIds = new ArrayList<>(); - for (Language language : LanguageRegistry.findWithRuleSupport()) { - Properties props = new Properties(); - rulesetsProperties = "category/" + language.getTerseName() + "/categories.properties"; - try (InputStream inputStream = resourceLoader.loadClassPathResourceAsStreamOrThrow(rulesetsProperties)) { - props.load(inputStream); - String rulesetFilenames = props.getProperty("rulesets.filenames"); - if (rulesetFilenames != null) { - ruleSetReferenceIds.addAll(RuleSetReferenceId.parse(rulesetFilenames)); - } - } catch (RuleSetNotFoundException e) { - LOG.warning("The language " + language.getTerseName() + " provides no " + rulesetsProperties + "."); - } catch (IOException ioe) { - throw new RuntimeException("Couldn't find " + rulesetsProperties - + "; please ensure that the directory is on the classpath. The current classpath is: " - + System.getProperty("java.class.path")); - } - } - return createRuleSets(ruleSetReferenceIds).getRuleSetsIterator(); + return toLoader().getStandardRuleSets().iterator(); } /** @@ -564,7 +540,7 @@ public class RuleSetFactory { // load the ruleset with minimum priority low, so that we get all rules, to be able to exclude any rule // minimum priority will be applied again, before constructing the final ruleset - RuleSetFactory ruleSetFactory = toParser().filterAbovePriority(RulePriority.LOW).warnDeprecated(false).toFactory(); + RuleSetFactory ruleSetFactory = toLoader().filterAbovePriority(RulePriority.LOW).warnDeprecated(false).toFactory(); RuleSet otherRuleSet = ruleSetFactory.createRuleSet(RuleSetReferenceId.parse(ref).get(0)); List potentialRules = new ArrayList<>(); int countDeprecated = 0; @@ -679,7 +655,7 @@ public class RuleSetFactory { // load the ruleset with minimum priority low, so that we get all rules, to be able to exclude any rule // minimum priority will be applied again, before constructing the final ruleset - RuleSetFactory ruleSetFactory = toParser().filterAbovePriority(RulePriority.LOW).warnDeprecated(false).toFactory(); + RuleSetFactory ruleSetFactory = toLoader().filterAbovePriority(RulePriority.LOW).warnDeprecated(false).toFactory(); boolean isSameRuleSet = false; RuleSetReferenceId otherRuleSetReferenceId = RuleSetReferenceId.parse(ref).get(0); @@ -845,7 +821,7 @@ public class RuleSetFactory { * Create a new {@link RuleSetLoader} with the same config as this * factory. This is a transitional API. */ - public RuleSetLoader toParser() { + public RuleSetLoader toLoader() { return new RuleSetLoader().loadResourcesWith(resourceLoader) .filterAbovePriority(minimumPriority) .warnDeprecated(warnDeprecated) 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 34a876717b..032372ae7c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -4,11 +4,17 @@ package net.sourceforge.pmd; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Properties; +import java.util.logging.Logger; +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.util.ResourceLoader; /** @@ -19,6 +25,8 @@ import net.sourceforge.pmd.util.ResourceLoader; */ public final class RuleSetLoader { + private static final Logger LOG = Logger.getLogger(RuleSetLoader.class.getName()); + private ResourceLoader resourceLoader = new ResourceLoader(RuleSetLoader.class.getClassLoader()); private RulePriority minimumPriority = RulePriority.LOW; private boolean warnDeprecated = true; @@ -162,4 +170,38 @@ public final class RuleSetLoader { return new RuleSetLoader().filterAbovePriority(configuration.getMinimumPriority()) .enableCompatibility(configuration.isRuleSetFactoryCompatibilityEnabled()); } + + + /** + * Returns an Iterator of RuleSet objects loaded from descriptions from the + * "categories.properties" resource for each Language with Rule support. This + * uses the classpath of the resource loader ({@link #loadResourcesWith(ClassLoader)}). + * + * @return A list of all category rulesets + * + * @throws RuleSetNotFoundException if some ruleset file could not be parsed + * TODO shouldn't our API forbid this case? + */ + public List getStandardRuleSets() throws RuleSetNotFoundException { + String rulesetsProperties; + List ruleSetReferenceIds = new ArrayList<>(); + for (Language language : LanguageRegistry.findWithRuleSupport()) { + Properties props = new Properties(); + rulesetsProperties = "category/" + language.getTerseName() + "/categories.properties"; + try (InputStream inputStream = resourceLoader.loadClassPathResourceAsStreamOrThrow(rulesetsProperties)) { + props.load(inputStream); + String rulesetFilenames = props.getProperty("rulesets.filenames"); + if (rulesetFilenames != null) { + ruleSetReferenceIds.addAll(RuleSetReferenceId.parse(rulesetFilenames)); + } + } catch (RuleSetNotFoundException e) { + LOG.fine("The language " + language.getTerseName() + " provides no " + rulesetsProperties + "."); + } catch (IOException ioe) { + throw new RuleSetNotFoundException("Couldn't read " + rulesetsProperties + + "; please ensure that the directory is on the classpath. The current classpath is: " + + System.getProperty("java.class.path"), ioe); + } + } + return toFactory().createRuleSets(ruleSetReferenceIds).getRuleSetsInternal(); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java index 0ad22f4c68..5c86702518 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java @@ -10,4 +10,8 @@ public class RuleSetNotFoundException extends Exception { public RuleSetNotFoundException(String msg) { super(msg); } + + public RuleSetNotFoundException(String msg, Throwable cause) { + super(msg, cause); + } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java index c0e43ddb10..c107765f0a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSets.java @@ -95,6 +95,11 @@ public class RuleSets { return ruleSets.toArray(new RuleSet[0]); } + // internal + List getRuleSetsInternal() { + return ruleSets; + } + public Iterator getRuleSetsIterator() { return ruleSets.iterator(); } From db7bf0a786954ee065a1ac0f4959cd9ac13cfbc7 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Thu, 12 Nov 2020 10:15:04 -0800 Subject: [PATCH 28/98] Store type information in AST instead of map Store the IdentifierType on ASTIdentifier node instead of in a separate map. Use the existing TypeResolution pattern to configure the visitor instead deriving from an abstract rule. Changed ParserOptions to extend AbstractPropertySource with the ability to override the defaults via environment variables. --- .../sourceforge/pmd/lang/ParserOptions.java | 73 ++- .../properties/AbstractPropertySource.java | 19 + .../pmd/lang/ParserOptionsTest.java | 154 ++++++ .../pmd/lang/vf/ApexClassPropertyTypes.java | 12 +- .../pmd/lang/vf/IdentifierType.java | 10 +- .../pmd/lang/vf/ObjectFieldTypes.java | 16 +- .../pmd/lang/vf/VfExpressionTypeVisitor.java | 56 ++- .../sourceforge/pmd/lang/vf/VfHandler.java | 18 + .../net/sourceforge/pmd/lang/vf/VfParser.java | 7 +- .../pmd/lang/vf/VfParserOptions.java | 16 + .../pmd/lang/vf/ast/ASTCompilationUnit.java | 16 + .../pmd/lang/vf/ast/ASTIdentifier.java | 14 + .../AbstractVfTypedElExpressionRule.java | 89 ---- .../vf/rule/security/VfUnescapeElRule.java | 9 +- .../lang/vf/ApexClassPropertyTypesTest.java | 32 +- .../vf/ApexClassPropertyTypesVisitorTest.java | 4 +- .../pmd/lang/vf/ObjectFieldTypesTest.java | 21 +- .../pmd/lang/vf/VFTestContstants.java | 28 -- .../sourceforge/pmd/lang/vf/VFTestUtils.java | 66 +++ .../pmd/lang/vf/VfParserOptionsTest.java | 20 + .../vf/rule/security/VfUnescapeElTest.java | 31 +- .../metadata/sfdx}/classes/ApexController.cls | 0 .../ApexWithConflictingPropertyTypes.cls | 0 .../metadata/sfdx/pages/SomePage.page | 1 + .../metadata/sfdx/classes/ApexController.cls | 39 ++ .../metadata/mdapi}/objects/Account.object | 0 .../metadata/mdapi/pages/SomePage.page | 1 + .../Account/fields/Checkbox__c.field-meta.xml | 0 .../Account/fields/DateTime__c.field-meta.xml | 0 .../fields/LongTextArea__c.field-meta.xml | 0 .../Account/fields/Picklist__c.field-meta.xml | 0 .../Account/fields/TextArea__c.field-meta.xml | 0 .../Account/fields/Text__c.field-meta.xml | 0 .../metadata/sfdx/pages/SomePage.page | 1 + .../metadata/mdapi/objects/Account.object | 445 ++++++++++++++++++ .../mdapi}/pages/StandardAccount.page | 0 .../metadata/sfdx/classes/ApexController.cls | 39 ++ .../metadata/sfdx}/classes/ApexExtension1.cls | 0 .../metadata/sfdx}/classes/ApexExtension2.cls | 0 .../Account/fields/Checkbox__c.field-meta.xml | 9 + .../Account/fields/DateTime__c.field-meta.xml | 9 + .../fields/LongTextArea__c.field-meta.xml | 10 + .../Account/fields/Picklist__c.field-meta.xml | 24 + .../Account/fields/TextArea__c.field-meta.xml | 9 + .../Account/fields/Text__c.field-meta.xml | 11 + .../metadata/sfdx}/pages/ApexController.page | 0 .../metadata/sfdx}/pages/StandardAccount.page | 0 .../pages/StandardAccountWithExtensions.page | 0 48 files changed, 1104 insertions(+), 205 deletions(-) create mode 100644 pmd-core/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java delete mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java delete mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestContstants.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestUtils.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => ApexClassPropertyTypes/metadata/sfdx}/classes/ApexController.cls (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => ApexClassPropertyTypes/metadata/sfdx}/classes/ApexWithConflictingPropertyTypes.cls (100%) create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor/metadata/sfdx/classes/ApexController.cls rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-mdapi => ObjectFieldTypes/metadata/mdapi}/objects/Account.object (100%) create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => ObjectFieldTypes/metadata/sfdx}/objects/Account/fields/Checkbox__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => ObjectFieldTypes/metadata/sfdx}/objects/Account/fields/DateTime__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => ObjectFieldTypes/metadata/sfdx}/objects/Account/fields/LongTextArea__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => ObjectFieldTypes/metadata/sfdx}/objects/Account/fields/Picklist__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => ObjectFieldTypes/metadata/sfdx}/objects/Account/fields/TextArea__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => ObjectFieldTypes/metadata/sfdx}/objects/Account/fields/Text__c.field-meta.xml (100%) create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/objects/Account.object rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-mdapi => rule/security/VfUnescapeEl/metadata/mdapi}/pages/StandardAccount.page (100%) create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexController.cls rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => rule/security/VfUnescapeEl/metadata/sfdx}/classes/ApexExtension1.cls (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => rule/security/VfUnescapeEl/metadata/sfdx}/classes/ApexExtension2.cls (100%) create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => rule/security/VfUnescapeEl/metadata/sfdx}/pages/ApexController.page (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => rule/security/VfUnescapeEl/metadata/sfdx}/pages/StandardAccount.page (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{metadata-sfdx => rule/security/VfUnescapeEl/metadata/sfdx}/pages/StandardAccountWithExtensions.page (100%) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java index e379d05e5f..3b3c55d7d6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java @@ -4,15 +4,34 @@ package net.sourceforge.pmd.lang; +import java.util.Locale; +import java.util.Objects; + +import net.sourceforge.pmd.properties.AbstractPropertySource; +import net.sourceforge.pmd.properties.PropertyDescriptor; + /** * Represents a set of configuration options for a {@link Parser}. For each * unique combination of ParserOptions a Parser will be used to create an AST. * Therefore, implementations must implement {@link Object#equals(Object)} and * {@link Object#hashCode()}. */ -public class ParserOptions { +public class ParserOptions extends AbstractPropertySource { protected String suppressMarker; + /** + * Language used to construct environment variable names that match PropertyDescriptors. + */ + private final Language language; + + public ParserOptions() { + this(null); + } + + public ParserOptions(Language language) { + this.language = language; + } + public String getSuppressMarker() { return suppressMarker; } @@ -29,12 +48,60 @@ public class ParserOptions { if (obj == null || getClass() != obj.getClass()) { return false; } + if (!super.equals(obj)) { + return false; + } final ParserOptions that = (ParserOptions) obj; - return this.suppressMarker.equals(that.suppressMarker); + return Objects.equals(suppressMarker, that.suppressMarker) + && Objects.equals(language, that.language); } @Override public int hashCode() { - return suppressMarker != null ? suppressMarker.hashCode() : 0; + return Objects.hash(super.hashCode(), suppressMarker, language); + } + + @Override + protected String getPropertySourceType() { + return "ParserOptions"; + } + + @Override + public String getName() { + return getClass().getSimpleName(); + } + + /** + * Returns the environment variable name that a user can set in order to override the default value. + */ + String getEnvironmentVariableName(PropertyDescriptor propertyDescriptor) { + if (language == null) { + throw new IllegalStateException("Language is null"); + } + return "PMD_" + language.getTerseName().toUpperCase(Locale.ROOT) + "_" + + propertyDescriptor.name().toUpperCase(Locale.ROOT); + } + + /** + * @return environment variable that overrides the PropertyDesciptors default value. Returns null if no environment + * variable has been set. + */ + protected String getEnvValue(PropertyDescriptor propertyDescriptor) { + return System.getenv(getEnvironmentVariableName(propertyDescriptor)); + } + + /** + * Overrides the default PropertyDescriptors with values found in environment variables. + * TODO: Move this to net.sourceforge.pmd.PMD#parserFor when CLI options are implemented + */ + protected void overridePropertiesFromEnv() { + for (PropertyDescriptor propertyDescriptor : getPropertyDescriptors()) { + String propertyValue = getEnvValue(propertyDescriptor); + + if (propertyValue != null) { + Object value = propertyDescriptor.valueFrom(propertyValue); + setProperty(propertyDescriptor, value); + } + } } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java index 71463940cf..1a1bc05333 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java @@ -11,6 +11,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import net.sourceforge.pmd.util.CollectionUtil; @@ -252,4 +253,22 @@ public abstract class AbstractPropertySource implements PropertySource { private String errorForPropCapture(PropertyDescriptor descriptor) { return descriptor.errorFor(getProperty(descriptor)); } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final AbstractPropertySource that = (AbstractPropertySource) obj; + return Objects.equals(propertyDescriptors, that.propertyDescriptors) + && Objects.equals(propertyValuesByDescriptor, that.propertyValuesByDescriptor); + } + + @Override + public int hashCode() { + return Objects.hash(propertyDescriptors, propertyValuesByDescriptor); + } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java new file mode 100644 index 0000000000..1e5e2fe1b8 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java @@ -0,0 +1,154 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.junit.Test; + +import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertyFactory; + +public class ParserOptionsTest { + private static final List DEFAULT_LIST = Arrays.asList("value1", "value2"); + private static final String DEFAULT_STRING = "value3"; + private static final List OVERRIDDEN_LIST = Arrays.asList("override1", "override2"); + private static final String OVERRIDDEN_STRING = "override3"; + + private static class TestParserOptions extends ParserOptions { + private static final PropertyDescriptor> LIST_DESCRIPTOR = + PropertyFactory.stringListProperty("listOfStringValues") + .desc("A list of values for testing.") + .defaultValue(DEFAULT_LIST) + .delim(',') + .build(); + + private static final PropertyDescriptor STRING_DESCRIPTOR = + PropertyFactory.stringProperty("stringValue") + .desc("A single value for testing.") + .defaultValue(DEFAULT_STRING) + .build(); + + private TestParserOptions() { + super(new DummyLanguageModule()); + definePropertyDescriptor(LIST_DESCRIPTOR); + definePropertyDescriptor(STRING_DESCRIPTOR); + overridePropertiesFromEnv(); + } + } + + @Test + public void testDefaultPropertyDescriptors() { + TestParserOptions parserOptions = new TestParserOptions(); + assertEquals(DEFAULT_LIST, parserOptions.getProperty(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals(DEFAULT_STRING, parserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); + assertEquals("ParserOptions", parserOptions.getPropertySourceType()); + assertEquals("TestParserOptions", parserOptions.getName()); + } + + @Test + public void testOverriddenPropertyDescriptors() { + TestParserOptions parserOptions = new TestParserOptions(); + parserOptions.setProperty(TestParserOptions.LIST_DESCRIPTOR, OVERRIDDEN_LIST); + parserOptions.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + + assertEquals(OVERRIDDEN_LIST, parserOptions.getProperty(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals(OVERRIDDEN_STRING, parserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); + } + + @Test + public void testEnvOverriddenPropertyDescriptors() { + TestParserOptions parserOptions = new TestParserOptions() { + @Override + protected String getEnvValue(PropertyDescriptor propertyDescriptor) { + if (propertyDescriptor.equals(TestParserOptions.LIST_DESCRIPTOR)) { + return StringUtils.join(OVERRIDDEN_LIST, ","); + } else if (propertyDescriptor.equals(TestParserOptions.STRING_DESCRIPTOR)) { + return OVERRIDDEN_STRING; + } else { + throw new RuntimeException("Should not happen"); + } + } + }; + + assertEquals(OVERRIDDEN_LIST, parserOptions.getProperty(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals(OVERRIDDEN_STRING, parserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); + } + + @Test + public void testEmptyPropertyDescriptors() { + TestParserOptions vfParserOptions = new TestParserOptions() { + @Override + protected String getEnvValue(PropertyDescriptor propertyDescriptor) { + if (propertyDescriptor.equals(TestParserOptions.LIST_DESCRIPTOR) + || propertyDescriptor.equals(TestParserOptions.STRING_DESCRIPTOR)) { + return ""; + } else { + throw new RuntimeException("Should not happen"); + } + } + }; + + assertEquals(Collections.emptyList(), vfParserOptions.getProperty(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals("", vfParserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); + } + + @Test + public void testEqualsAndHashCode() { + ParserOptions parserOptions = new ParserOptions(); + TestParserOptions testParserOptions1 = new TestParserOptions(); + TestParserOptions testParserOptions2 = new TestParserOptions(); + + // Differences based on Language + assertNotNull(parserOptions.hashCode()); + assertFalse(parserOptions.equals(testParserOptions1)); + assertNotEquals(parserOptions.hashCode(), testParserOptions1.hashCode()); + + // Differences based on Properties + assertNotNull(testParserOptions1.hashCode()); + assertTrue(testParserOptions1.equals(testParserOptions2)); + assertEquals(testParserOptions1.hashCode(), testParserOptions2.hashCode()); + + testParserOptions1.setProperty(TestParserOptions.LIST_DESCRIPTOR, OVERRIDDEN_LIST); + assertFalse(testParserOptions1.equals(testParserOptions2)); + assertNotEquals(testParserOptions1.hashCode(), testParserOptions2.hashCode()); + + testParserOptions1.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + assertFalse(testParserOptions1.equals(testParserOptions2)); + assertNotEquals(testParserOptions1.hashCode(), testParserOptions2.hashCode()); + + testParserOptions2.setProperty(TestParserOptions.LIST_DESCRIPTOR, OVERRIDDEN_LIST); + assertFalse(testParserOptions1.equals(testParserOptions2)); + assertNotEquals(testParserOptions1.hashCode(), testParserOptions2.hashCode()); + + testParserOptions2.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + assertTrue(testParserOptions1.equals(testParserOptions2)); + assertEquals(testParserOptions1.hashCode(), testParserOptions2.hashCode()); + } + + @Test + public void testGetEnvironmentVariableName() { + ParserOptions parserOptions = new TestParserOptions(); + assertEquals("PMD_DUMMY_LISTOFSTRINGVALUES", + parserOptions.getEnvironmentVariableName(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals("PMD_DUMMY_STRINGVALUE", + parserOptions.getEnvironmentVariableName(TestParserOptions.STRING_DESCRIPTOR)); + } + + @Test(expected = IllegalStateException.class) + public void testGetEnvironmentVariableNameThrowsExceptionIfLanguageIsNull() { + ParserOptions parserOptions = new ParserOptions(); + parserOptions.getEnvironmentVariableName(TestParserOptions.LIST_DESCRIPTOR); + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java index f82e517cb8..d44a6f2571 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java @@ -10,11 +10,12 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import org.apache.commons.lang3.tuple.Pair; @@ -37,12 +38,12 @@ class ApexClassPropertyTypes { private static final Logger LOGGER = Logger.getLogger(ApexClassPropertyTypes.class.getName()); private static final String APEX_CLASS_FILE_SUFFIX = ".cls"; - private final ConcurrentHashMap variableNameToVariableType; + private final Map variableNameToVariableType; private final Set variableNameProcessed; ApexClassPropertyTypes() { - this.variableNameToVariableType = new ConcurrentHashMap<>(); - this.variableNameProcessed = Collections.newSetFromMap(new ConcurrentHashMap()); + this.variableNameToVariableType = new HashMap<>(); + this.variableNameProcessed = new HashSet<>(); } /** @@ -128,4 +129,3 @@ class ApexClassPropertyTypes { return languageVersion.getLanguageVersionHandler().getParser(parserOptions); } } - diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/IdentifierType.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/IdentifierType.java index 091a74202c..ec95ced18b 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/IdentifierType.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/IdentifierType.java @@ -5,11 +5,11 @@ package net.sourceforge.pmd.lang.vf; import java.util.Arrays; -import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import apex.jorje.semantic.symbol.type.BasicType; @@ -68,12 +68,12 @@ public enum IdentifierType { * A case insensitive map of the enum name to its instance. The case metadata is not guaranteed to have the correct * case. */ - private static final Map CASE_INSENSITIVE_MAP = new ConcurrentHashMap<>(); + private static final Map CASE_INSENSITIVE_MAP = new HashMap<>(); /** * Map of BasicType to IdentifierType. Multiple BasicTypes may map to one ExrpessionType. */ - private static final Map BASIC_TYPE_MAP = new ConcurrentHashMap<>(); + private static final Map BASIC_TYPE_MAP = new HashMap<>(); static { for (IdentifierType identifierType : IdentifierType.values()) { @@ -119,7 +119,7 @@ public enum IdentifierType { IdentifierType(boolean requiresEscaping, BasicType...basicTypes) { this.requiresEscaping = requiresEscaping; - this.basicTypes = Collections.newSetFromMap(new ConcurrentHashMap()); + this.basicTypes = new HashSet<>(); if (basicTypes != null) { this.basicTypes.addAll(Arrays.asList(basicTypes)); } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java index 1a047c6b17..6ff6f5cc27 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java @@ -7,12 +7,12 @@ package net.sourceforge.pmd.lang.vf; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -41,7 +41,7 @@ class ObjectFieldTypes { private static final Map STANDARD_FIELD_TYPES; static { - STANDARD_FIELD_TYPES = new ConcurrentHashMap<>(); + STANDARD_FIELD_TYPES = new HashMap<>(); STANDARD_FIELD_TYPES.put("createdbyid", IdentifierType.Lookup); STANDARD_FIELD_TYPES.put("createddate", IdentifierType.DateTime); STANDARD_FIELD_TYPES.put("id", IdentifierType.Lookup); @@ -54,7 +54,7 @@ class ObjectFieldTypes { /** * Cache of lowercase variable names to the variable type declared in the field's metadata file. */ - private final ConcurrentHashMap variableNameToVariableType; + private final Map variableNameToVariableType; /** * Keep track of which variables were already processed. Avoid processing if a page repeatedly asks for an entry @@ -77,9 +77,9 @@ class ObjectFieldTypes { private final XPathExpression sfdxCustomFieldTypeExpression; ObjectFieldTypes() { - this.variableNameToVariableType = new ConcurrentHashMap<>(); - this.variableNameProcessed = Collections.newSetFromMap(new ConcurrentHashMap()); - this.objectFileProcessed = Collections.newSetFromMap(new ConcurrentHashMap()); + this.variableNameToVariableType = new HashMap<>(); + this.variableNameProcessed = new HashSet<>(); + this.objectFileProcessed = new HashSet<>(); try { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); @@ -267,7 +267,7 @@ class ObjectFieldTypes { name = name.toLowerCase(Locale.ROOT); IdentifierType previousType = variableNameToVariableType.put(name, identifierType); if (previousType != null && !previousType.equals(identifierType)) { - // It should not be possible ot have conflicting types for CustomFields + // It should not be possible to have conflicting types for CustomFields throw new RuntimeException("Conflicting types for " + name + ". CurrentType=" diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java index 44289b6829..f17e4cb5ef 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java @@ -4,15 +4,19 @@ package net.sourceforge.pmd.lang.vf; +import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.logging.Logger; +import net.sourceforge.pmd.lang.ast.AbstractTokenManager; import net.sourceforge.pmd.lang.vf.ast.ASTAttribute; import net.sourceforge.pmd.lang.vf.ast.ASTAttributeValue; +import net.sourceforge.pmd.lang.vf.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.vf.ast.ASTDotExpression; import net.sourceforge.pmd.lang.vf.ast.ASTElExpression; import net.sourceforge.pmd.lang.vf.ast.ASTElement; @@ -20,6 +24,9 @@ import net.sourceforge.pmd.lang.vf.ast.ASTExpression; import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; import net.sourceforge.pmd.lang.vf.ast.ASTText; import net.sourceforge.pmd.lang.vf.ast.VfParserVisitorAdapter; +import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertyFactory; +import net.sourceforge.pmd.properties.PropertySource; /** * Visits {@link ASTElExpression} nodes and stores type information for all {@link ASTIdentifier} child nodes. @@ -27,16 +34,39 @@ import net.sourceforge.pmd.lang.vf.ast.VfParserVisitorAdapter; public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { private static final Logger LOGGER = Logger.getLogger(VfExpressionTypeVisitor.class.getName()); + static final List DEFAULT_APEX_DIRECTORIES = Collections.singletonList(".." + File.separator + "classes"); + static final List DEFAULT_OBJECT_DIRECTORIES = Collections.singletonList(".." + File.separator + "objects"); + + /** + * Directory that contains Apex classes that may be referenced from a Visualforce page. + */ + public static final PropertyDescriptor> APEX_DIRECTORIES_DESCRIPTOR = + PropertyFactory.stringListProperty("apexDirectories") + .desc("Location of Apex Class directories. Absolute or relative to the Visualforce directory.") + .defaultValue(DEFAULT_APEX_DIRECTORIES) + .delim(',') + .build(); + + /** + * Directory that contains Object definitions that may be referenced from a Visualforce page. + */ + public static final PropertyDescriptor> OBJECTS_DIRECTORIES_DESCRIPTOR = + PropertyFactory.stringListProperty("objectsDirectories") + .desc("Location of CustomObject directories. Absolute or relative to the Visualforce directory.") + .defaultValue(DEFAULT_OBJECT_DIRECTORIES) + .delim(',') + .build(); + private static final String APEX_PAGE = "apex:page"; private static final String CONTROLLER_ATTRIBUTE = "controller"; private static final String STANDARD_CONTROLLER_ATTRIBUTE = "standardcontroller"; private static final String EXTENSIONS_ATTRIBUTE = "extensions"; - private ApexClassPropertyTypes apexClassPropertyTypes; - private ObjectFieldTypes objectFieldTypes; - private final String fileName; + private final ApexClassPropertyTypes apexClassPropertyTypes; + private final ObjectFieldTypes objectFieldTypes; + + private String fileName; private String standardControllerName; - private final IdentityHashMap identifierTypes; /** * List of all Apex Class names that the VF page might refer to. These values come from either the @@ -46,18 +76,18 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { private final List apexDirectories; private final List objectsDirectories; - public VfExpressionTypeVisitor(String fileName, List apexDirectories, List objectsDirectories) { - this.fileName = fileName; - this.apexDirectories = apexDirectories; - this.objectsDirectories = objectsDirectories; + public VfExpressionTypeVisitor(PropertySource propertySource) { + this.apexDirectories = propertySource.getProperty(APEX_DIRECTORIES_DESCRIPTOR); + this.objectsDirectories = propertySource.getProperty(OBJECTS_DIRECTORIES_DESCRIPTOR); + this.apexClassNames = new ArrayList<>(); this.apexClassPropertyTypes = new ApexClassPropertyTypes(); this.objectFieldTypes = new ObjectFieldTypes(); - this.apexClassNames = new ArrayList<>(); - this.identifierTypes = new IdentityHashMap<>(); } - public IdentityHashMap getIdentifierTypes() { - return this.identifierTypes; + @Override + public Object visit(ASTCompilationUnit node, Object data) { + this.fileName = AbstractTokenManager.getFileName(); + return super.visit(node, data); } /** @@ -139,7 +169,7 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { } if (type != null) { - identifierTypes.put(entry.getKey(), type); + entry.getKey().setIdentifierType(type); } else { LOGGER.fine("Unable to determine type for: " + name); } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java index e40d90f8ab..e7bfc2c2fe 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java @@ -12,6 +12,7 @@ import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.VisitorStarter; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; +import net.sourceforge.pmd.lang.vf.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.vf.ast.DumpFacade; import net.sourceforge.pmd.lang.vf.ast.VfNode; import net.sourceforge.pmd.lang.vf.rule.VfRuleViolationFactory; @@ -28,6 +29,23 @@ public class VfHandler extends AbstractLanguageVersionHandler { return new VfParser(parserOptions); } + @Override + public VisitorStarter getTypeResolutionFacade(ClassLoader classLoader) { + return new VisitorStarter() { + @Override + public void start(Node rootNode) { + ASTCompilationUnit astCompilationUnit = (ASTCompilationUnit) rootNode; + VfExpressionTypeVisitor visitor = new VfExpressionTypeVisitor(astCompilationUnit.getPropertySource()); + visitor.visit((ASTCompilationUnit) rootNode, null); + } + }; + } + + @Override + public ParserOptions getDefaultParserOptions() { + return new VfParserOptions(); + } + @Deprecated @Override public VisitorStarter getDumpFacade(final Writer writer, final String prefix, final boolean recurse) { diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java index e40938a291..0c14ab733f 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java @@ -17,6 +17,7 @@ import net.sourceforge.pmd.lang.ast.AbstractTokenManager; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.SimpleCharStream; +import net.sourceforge.pmd.lang.vf.ast.ASTCompilationUnit; /** * Adapter for the VfParser. @@ -26,7 +27,6 @@ import net.sourceforge.pmd.lang.ast.SimpleCharStream; @Deprecated @InternalApi public class VfParser extends AbstractParser { - public VfParser(ParserOptions parserOptions) { super(parserOptions); } @@ -44,7 +44,10 @@ public class VfParser extends AbstractParser { @Override public Node parse(String fileName, Reader source) throws ParseException { AbstractTokenManager.setFileName(fileName); - return new net.sourceforge.pmd.lang.vf.ast.VfParser(new SimpleCharStream(source)).CompilationUnit(); + ASTCompilationUnit astCompilationUnit = new net.sourceforge.pmd.lang.vf.ast.VfParser( + new SimpleCharStream(source)).CompilationUnit(); + astCompilationUnit.setPropertySource(this.getParserOptions()); + return astCompilationUnit; } @Override diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java new file mode 100644 index 0000000000..d6a7a91e48 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java @@ -0,0 +1,16 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import net.sourceforge.pmd.lang.ParserOptions; + +public class VfParserOptions extends ParserOptions { + public VfParserOptions() { + super(new VfLanguageModule()); + definePropertyDescriptor(VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR); + definePropertyDescriptor(VfExpressionTypeVisitor.OBJECTS_DIRECTORIES_DESCRIPTOR); + overridePropertiesFromEnv(); + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTCompilationUnit.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTCompilationUnit.java index 65827db0a5..2d57e0db00 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTCompilationUnit.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTCompilationUnit.java @@ -6,8 +6,16 @@ package net.sourceforge.pmd.lang.vf.ast; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.RootNode; +import net.sourceforge.pmd.properties.PropertySource; public class ASTCompilationUnit extends AbstractVFNode implements RootNode { + /** + * Holds the properties contained in {@code VfParserOptions}. This class acts as an intermediary since it is + * accessible by {@code VFParser} and {@code VFHandler}. {@code VfHandler} uses this value to initialize the + * {@code VfExpressionTypeVisitor} for type resolution. + */ + private PropertySource propertySource; + @Deprecated @InternalApi public ASTCompilationUnit(int id) { @@ -24,4 +32,12 @@ public class ASTCompilationUnit extends AbstractVFNode implements RootNode { public Object jjtAccept(VfParserVisitor visitor, Object data) { return visitor.visit(this, data); } + + public PropertySource getPropertySource() { + return propertySource; + } + + public void setPropertySource(PropertySource propertySource) { + this.propertySource = propertySource; + } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTIdentifier.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTIdentifier.java index fa7d70601c..21541527bb 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTIdentifier.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTIdentifier.java @@ -5,8 +5,14 @@ package net.sourceforge.pmd.lang.vf.ast; import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.vf.IdentifierType; public class ASTIdentifier extends AbstractVFNode { + /** + * The data type that this identifier refers to. May be null. + */ + private IdentifierType identifierType; + @Deprecated @InternalApi public ASTIdentifier(int id) { @@ -23,4 +29,12 @@ public class ASTIdentifier extends AbstractVFNode { public Object jjtAccept(VfParserVisitor visitor, Object data) { return visitor.visit(this, data); } + + public IdentifierType getIdentifierType() { + return identifierType; + } + + public void setIdentifierType(IdentifierType identifierType) { + this.identifierType = identifierType; + } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java deleted file mode 100644 index 0d012576a1..0000000000 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/AbstractVfTypedElExpressionRule.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.vf.rule.security; - -import java.io.File; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.List; -import java.util.Map; - -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.vf.IdentifierType; -import net.sourceforge.pmd.lang.vf.VfExpressionTypeVisitor; -import net.sourceforge.pmd.lang.vf.ast.ASTCompilationUnit; -import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; -import net.sourceforge.pmd.lang.vf.rule.AbstractVfRule; -import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertyFactory; - -/** - * Represents a rule where the {@link net.sourceforge.pmd.lang.vf.ast.ASTIdentifier} nodes are enhanced with the - * node's {@link IdentifierType}. This is achieved by processing metadata files referenced by the Visualforce page. - */ -abstract class AbstractVfTypedElExpressionRule extends AbstractVfRule { - /** - * Directory that contains Apex classes that may be referenced from a Visualforce page. - */ - private static final PropertyDescriptor> APEX_DIRECTORIES_DESCRIPTOR = - PropertyFactory.stringListProperty("apexDirectories") - .desc("Location of Apex Class directories. Absolute or relative to the Visualforce directory") - .defaultValue(Collections.singletonList(".." + File.separator + "classes")) - .delim(',') - .build(); - - /** - * Directory that contains Object definitions that may be referenced from a Visualforce page. - */ - private static final PropertyDescriptor> OBJECTS_DIRECTORIES_DESCRIPTOR = - PropertyFactory.stringListProperty("objectsDirectories") - .desc("Location of CustomObject directories. Absolute or relative to the Visualforce directory") - .defaultValue(Collections.singletonList(".." + File.separator + "objects")) - .delim(',') - .build(); - - private Map identifierTypes; - - AbstractVfTypedElExpressionRule() { - definePropertyDescriptor(APEX_DIRECTORIES_DESCRIPTOR); - definePropertyDescriptor(OBJECTS_DIRECTORIES_DESCRIPTOR); - } - - public IdentifierType getIdentifierType(ASTIdentifier node) { - return identifierTypes.get(node); - } - - @Override - public void start(RuleContext ctx) { - this.identifierTypes = Collections.synchronizedMap(new IdentityHashMap()); - super.start(ctx); - } - - /** - * Invoke {@link VfExpressionTypeVisitor#visit(ASTCompilationUnit, Object)} to identify Visualforce expression's and - * their types. - */ - @Override - public Object visit(ASTCompilationUnit node, Object data) { - List apexDirectories = getProperty(APEX_DIRECTORIES_DESCRIPTOR); - List objectsDirectories = getProperty(OBJECTS_DIRECTORIES_DESCRIPTOR); - - // The visitor will only find information if there are directories to look in. This allows users to disable the - // visitor in the unlikely scenario that they want to. - if (!apexDirectories.isEmpty() || !objectsDirectories.isEmpty()) { - RuleContext ctx = (RuleContext) data; - File file = ctx.getSourceCodeFile(); - if (file != null) { - VfExpressionTypeVisitor visitor = new VfExpressionTypeVisitor(file.getAbsolutePath(), - apexDirectories, - objectsDirectories); - visitor.visit(node, data); - this.identifierTypes.putAll(visitor.getIdentifierTypes()); - } - } - - return super.visit(node, data); - } -} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java index a19edd024d..ccda6bb4d6 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java @@ -26,12 +26,13 @@ import net.sourceforge.pmd.lang.vf.ast.ASTLiteral; import net.sourceforge.pmd.lang.vf.ast.ASTNegationExpression; import net.sourceforge.pmd.lang.vf.ast.ASTText; import net.sourceforge.pmd.lang.vf.ast.AbstractVFNode; +import net.sourceforge.pmd.lang.vf.rule.AbstractVfRule; /** * @author sergey.gorbaty February 2017 * */ -public class VfUnescapeElRule extends AbstractVfTypedElExpressionRule { +public class VfUnescapeElRule extends AbstractVfRule { private static final String A_CONST = "a"; private static final String APEXIFRAME_CONST = "apex:iframe"; private static final String IFRAME_CONST = "iframe"; @@ -50,6 +51,10 @@ public class VfUnescapeElRule extends AbstractVfTypedElExpressionRule { private static final Pattern ON_EVENT = Pattern.compile("^on(\\w)+$"); private static final Pattern PLACEHOLDERS = Pattern.compile("\\{(\\w|,|\\.|'|:|\\s)*\\}"); + public VfUnescapeElRule() { + this.setTypeResolution(true); + } + @Override public Object visit(ASTHtmlScript node, Object data) { checkIfCorrectlyEscaped(node, data); @@ -411,7 +416,7 @@ public class VfUnescapeElRule extends AbstractVfTypedElExpressionRule { final List ids = expr.findChildrenOfType(ASTIdentifier.class); for (final ASTIdentifier id : ids) { - IdentifierType identifierType = getIdentifierType(id); + IdentifierType identifierType = id.getIdentifierType(); if (identifierType != null && !identifierType.requiresEscaping) { return false; } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java index 74765d17b1..f490c7fe68 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java @@ -17,8 +17,8 @@ import org.junit.Test; public class ApexClassPropertyTypesTest { @Test public void testApexClassIsProperlyParsed() { - Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "ApexController.page")) - .toAbsolutePath(); + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve("SomePage.page"); String vfFileName = vfPagePath.toString(); // Intentionally use the wrong case for property names to ensure that they can be found. The Apex class name @@ -27,35 +27,35 @@ public class ApexClassPropertyTypesTest { ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); assertEquals(IdentifierType.Lookup, apexClassPropertyTypes.getVariableType("ApexController.accOuntIdProp", vfFileName, - VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); assertEquals(IdentifierType.Lookup, apexClassPropertyTypes.getVariableType("ApexController.AcCountId", vfFileName, - VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); assertEquals(IdentifierType.Text, apexClassPropertyTypes.getVariableType("ApexController.AcCountname", vfFileName, - VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); // InnerController assertEquals("The class should be parsed to Unknown. It's not a valid expression on its own.", IdentifierType.Unknown, apexClassPropertyTypes.getVariableType("ApexController.innErController", vfFileName, - VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); assertEquals(IdentifierType.Lookup, apexClassPropertyTypes.getVariableType("ApexController.innErController.innErAccountIdProp", - vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + vfFileName, VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); assertEquals(IdentifierType.Lookup, apexClassPropertyTypes.getVariableType("ApexController.innErController.innErAccountid", - vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + vfFileName, VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); assertEquals(IdentifierType.Text, apexClassPropertyTypes.getVariableType("ApexController.innErController.innErAccountnAme", - vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + vfFileName, VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); assertNull("Invalid class should return null", apexClassPropertyTypes.getVariableType("unknownclass.invalidProperty", vfFileName, - VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); assertNull("Invalid class property should return null", apexClassPropertyTypes.getVariableType("ApexController.invalidProperty", vfFileName, - VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); } /** @@ -65,19 +65,19 @@ public class ApexClassPropertyTypesTest { */ @Test public void testConflictingPropertyTypesMapsToUnknown() { - Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "ApexController.page")) - .toAbsolutePath(); + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve("SomePage.page"); String vfFileName = vfPagePath.toString(); ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); assertEquals(IdentifierType.Unknown, apexClassPropertyTypes.getVariableType("ApexWithConflictingPropertyTypes.ConflictingProp", - vfFileName, VFTestContstants.RELATIVE_APEX_DIRECTORIES)); + vfFileName, VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); } @Test public void testInvalidDirectoryDoesNotCauseAnException() { - Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "ApexController.page")) - .toAbsolutePath(); + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve("SomePage.page"); String vfFileName = vfPagePath.toString(); List paths = Arrays.asList(Paths.get("..", "classes-does-not-exist").toString()); diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitorTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitorTest.java index 5d7ec00550..ef74216593 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitorTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitorTest.java @@ -13,7 +13,6 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.Hashtable; import java.util.List; import java.util.Map; @@ -38,7 +37,8 @@ public class ApexClassPropertyTypesVisitorTest { ParserOptions parserOptions = languageVersion.getLanguageVersionHandler().getDefaultParserOptions(); Parser parser = languageVersion.getLanguageVersionHandler().getParser(parserOptions); - Path apexPath = VFTestContstants.SFDX_PATH.resolve(Paths.get("classes", "ApexController.cls")).toAbsolutePath(); + Path apexPath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Apex) + .resolve("ApexController.cls").toAbsolutePath(); ApexClassPropertyTypesVisitor visitor = new ApexClassPropertyTypesVisitor(); try (BufferedReader reader = Files.newBufferedReader(apexPath, StandardCharsets.UTF_8)) { Node node = parser.parse(apexPath.toString(), reader); diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java index 16ab824de9..ba9dea1421 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java @@ -22,11 +22,10 @@ public class ObjectFieldTypesTest { */ @Test public void testSfdxAccountIsProperlyParsed() { - Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "StandarAccountPage.page")) - .toAbsolutePath(); + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf).resolve("SomePage.page"); ObjectFieldTypes objectFieldTypes = new ObjectFieldTypes(); - validateSfdxAccount(objectFieldTypes, vfPagePath, VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES); + validateSfdxAccount(objectFieldTypes, vfPagePath, VfExpressionTypeVisitor.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue()); } /** @@ -34,11 +33,10 @@ public class ObjectFieldTypesTest { */ @Test public void testMdapiAccountIsProperlyParsed() { - Path vfPagePath = VFTestContstants.MDAPI_PATH.resolve(Paths.get("pages", "StandarAccountPage.page")) - .toAbsolutePath(); + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.MDAPI, VFTestUtils.MetadataType.Vf).resolve("SomePage.page"); ObjectFieldTypes objectFieldTypes = new ObjectFieldTypes(); - validateMDAPIAccount(objectFieldTypes, vfPagePath, VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES); + validateMDAPIAccount(objectFieldTypes, vfPagePath, VfExpressionTypeVisitor.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue()); } /** @@ -47,11 +45,11 @@ public class ObjectFieldTypesTest { @Test public void testFieldsAreFoundInMultipleDirectories() { ObjectFieldTypes objectFieldTypes; - Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "StandarAccountPage.page")) - .toAbsolutePath(); + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve("SomePage.page"); - List paths = Arrays.asList(VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES.get(0), - VFTestContstants.ABSOLUTE_MDAPI_OBJECTS_DIRECTORIES.get(0)); + List paths = Arrays.asList(VfExpressionTypeVisitor.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue().get(0), + VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.MDAPI, VFTestUtils.MetadataType.Objects).toString()); objectFieldTypes = new ObjectFieldTypes(); validateSfdxAccount(objectFieldTypes, vfPagePath, paths); validateMDAPIAccount(objectFieldTypes, vfPagePath, paths); @@ -64,8 +62,7 @@ public class ObjectFieldTypesTest { @Test public void testInvalidDirectoryDoesNotCauseAnException() { - Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "StandarAccountPage.page")) - .toAbsolutePath(); + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf).resolve("SomePage.page"); String vfFileName = vfPagePath.toString(); List paths = Arrays.asList(Paths.get("..", "objects-does-not-exist").toString()); diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestContstants.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestContstants.java deleted file mode 100644 index 85839e1b9d..0000000000 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestContstants.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.vf; - -import java.io.File; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Collections; -import java.util.List; - -public final class VFTestContstants { - private VFTestContstants() { - } - - private static final Path ROOT_PATH = Paths.get("src", "test", "resources", "net", "sourceforge", - "pmd", "lang", "vf").toAbsolutePath(); - - public static final Path SFDX_PATH = ROOT_PATH.resolve("metadata-sfdx"); - - public static final Path MDAPI_PATH = ROOT_PATH.resolve("metadata-mdapi"); - public static final List ABSOLUTE_MDAPI_OBJECTS_DIRECTORIES = - Collections.singletonList(MDAPI_PATH.resolve("objects").toAbsolutePath().toString()); - - public static final List RELATIVE_APEX_DIRECTORIES = Collections.singletonList(".." + File.separator + "classes"); - public static final List RELATIVE_OBJECTS_DIRECTORIES = Collections.singletonList(".." + File.separator + "objects"); -} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestUtils.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestUtils.java new file mode 100644 index 0000000000..3259b83425 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestUtils.java @@ -0,0 +1,66 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import java.nio.file.Path; +import java.nio.file.Paths; + +public final class VFTestUtils { + /** + * Salesforce metadata is stored in two different formats, the newer sfdx form and the older mdapi format. Used to + * locate metadata on the file system during unit tests. + */ + public enum MetadataFormat { + SFDX("sfdx"), + MDAPI("mdapi"); + + public final String directoryName; + + MetadataFormat(String directoryName) { + this.directoryName = directoryName; + } + } + + /** + * Represents the metadata types that are referenced from unit tests. Used to locate metadata on the file system + * during unit tests. + */ + public enum MetadataType { + Apex("classes"), + Objects("objects"), + Vf("pages"); + + public final String directoryName; + + MetadataType(String directoryName) { + this.directoryName = directoryName; + } + } + + /** + * @return the path of the directory that matches the given parameters. The directory path is constructed using the + * following convention: + * src/test/resources/_decomposed_test_package_name_/_test_class_name_minus_Test_/metadata/_metadata_format_/_metadata_type_ + */ + public static Path getMetadataPath(Object testClazz, MetadataFormat metadataFormat, MetadataType metadataType) { + Path path = Paths.get("src", "test", "resources"); + // Decompose the test's package structure into directories + for (String directory : testClazz.getClass().getPackage().getName().split("\\.")) { + path = path.resolve(directory); + } + // Remove 'Test' from the class name + path = path.resolve(testClazz.getClass().getSimpleName().replaceFirst("Test$", "")); + // Append additional directories based on the MetadataFormat and MetadataType + path = path.resolve("metadata").resolve(metadataFormat.directoryName); + if (metadataType != null) { + path = path.resolve(metadataType.directoryName); + } + + return path.toAbsolutePath(); + } + + private VFTestUtils() { + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java new file mode 100644 index 0000000000..c20faef000 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java @@ -0,0 +1,20 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class VfParserOptionsTest { + @Test + public void testDefaultPropertyDescriptors() { + VfParserOptions vfParserOptions = new VfParserOptions(); + assertEquals(VfExpressionTypeVisitor.DEFAULT_APEX_DIRECTORIES, + vfParserOptions.getProperty(VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR)); + assertEquals(VfExpressionTypeVisitor.DEFAULT_OBJECT_DIRECTORIES, + vfParserOptions.getProperty(VfExpressionTypeVisitor.OBJECTS_DIRECTORIES_DESCRIPTOR)); + } +} 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 22af978c1b..df2567286a 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 @@ -30,9 +30,8 @@ import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.Parser; import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.vf.VFTestContstants; +import net.sourceforge.pmd.lang.vf.VFTestUtils; import net.sourceforge.pmd.lang.vf.VfLanguageModule; -import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.testframework.PmdRuleTst; public class VfUnescapeElTest extends PmdRuleTst { @@ -43,10 +42,10 @@ public class VfUnescapeElTest extends PmdRuleTst { */ @Test public void testSfdxCustomFields() throws IOException, PMDException { - Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "StandardAccount.page")).toAbsolutePath(); + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve("StandardAccount.page"); - Report report = runRule(vfPagePath, VFTestContstants.RELATIVE_APEX_DIRECTORIES, - VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES); + Report report = runRule(vfPagePath); List ruleViolations = report.getViolations(); assertEquals(6, ruleViolations.size()); int firstLineWithErrors = 7; @@ -62,10 +61,9 @@ public class VfUnescapeElTest extends PmdRuleTst { */ @Test public void testMdapiCustomFields() throws IOException, PMDException { - Path vfPagePath = VFTestContstants.MDAPI_PATH.resolve(Paths.get("pages", "StandardAccount.page")).toAbsolutePath(); + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.MDAPI, VFTestUtils.MetadataType.Vf).resolve("StandardAccount.page"); - Report report = runRule(vfPagePath, VFTestContstants.RELATIVE_APEX_DIRECTORIES, - VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES); + Report report = runRule(vfPagePath); List ruleViolations = report.getViolations(); assertEquals(6, ruleViolations.size()); int firstLineWithErrors = 8; @@ -81,10 +79,9 @@ public class VfUnescapeElTest extends PmdRuleTst { */ @Test public void testApexController() throws IOException, PMDException { - Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "ApexController.page")).toAbsolutePath(); + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf).resolve("ApexController.page"); - Report report = runRule(vfPagePath, VFTestContstants.RELATIVE_APEX_DIRECTORIES, - VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES); + Report report = runRule(vfPagePath); List ruleViolations = report.getViolations(); assertEquals(2, ruleViolations.size()); int firstLineWithErrors = 9; @@ -101,10 +98,10 @@ public class VfUnescapeElTest extends PmdRuleTst { */ @Test public void testExtensions() throws IOException, PMDException { - Path vfPagePath = VFTestContstants.SFDX_PATH.resolve(Paths.get("pages", "StandardAccountWithExtensions.page")).toAbsolutePath(); + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve(Paths.get("StandardAccountWithExtensions.page")); - Report report = runRule(vfPagePath, VFTestContstants.RELATIVE_APEX_DIRECTORIES, - VFTestContstants.RELATIVE_OBJECTS_DIRECTORIES); + Report report = runRule(vfPagePath); List ruleViolations = report.getViolations(); assertEquals(8, ruleViolations.size()); int firstLineWithErrors = 9; @@ -119,7 +116,7 @@ public class VfUnescapeElTest extends PmdRuleTst { * Runs a rule against a Visualforce page on the file system. This code is based on * {@link net.sourceforge.pmd.testframework.RuleTst#runTestFromString(String, Rule, Report, LanguageVersion, boolean)} */ - private Report runRule(Path vfPagePath, List apexDirectories, List objectsDirectories) throws FileNotFoundException, PMDException { + private Report runRule(Path vfPagePath) throws FileNotFoundException, PMDException { LanguageVersion languageVersion = LanguageRegistry.getLanguage(VfLanguageModule.NAME).getDefaultVersion(); ParserOptions parserOptions = languageVersion.getLanguageVersionHandler().getDefaultParserOptions(); Parser parser = languageVersion.getLanguageVersionHandler().getParser(parserOptions); @@ -146,10 +143,6 @@ public class VfUnescapeElTest extends PmdRuleTst { }); Rule rule = findRule("category/vf/security.xml", "VfUnescapeEl"); - PropertyDescriptor apexPropertyDescriptor = rule.getPropertyDescriptor("apexDirectories"); - PropertyDescriptor objectPropertyDescriptor = rule.getPropertyDescriptor("objectsDirectories"); - rule.setProperty(apexPropertyDescriptor, apexDirectories); - rule.setProperty(objectPropertyDescriptor, objectsDirectories); Report report = new Report(); RuleContext ctx = new RuleContext(); ctx.setReport(report); diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexController.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.cls similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexController.cls rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.cls diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexWithConflictingPropertyTypes.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexWithConflictingPropertyTypes.cls similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexWithConflictingPropertyTypes.cls rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexWithConflictingPropertyTypes.cls diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page new file mode 100644 index 0000000000..d1e8d3fb88 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page @@ -0,0 +1 @@ + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor/metadata/sfdx/classes/ApexController.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor/metadata/sfdx/classes/ApexController.cls new file mode 100644 index 0000000000..df449e3e6f --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor/metadata/sfdx/classes/ApexController.cls @@ -0,0 +1,39 @@ +public class ApexController { + public Id AccountIdProp { get; set; } + + public ApexController() { + acc = [SELECT Id, Name, Site FROM Account + WHERE Id = :ApexPages.currentPage().getParameters().get('id')]; + this.AccountIdProp = acc.Id; + } + + public Id getAccountId() { + return acc.id; + } + + public String getAccountName() { + return acc.name; + } + + public InnerController getInnerController() { + return new InnerController(this); + } + + public class InnerController { + private ApexController parent; + public Id InnerAccountIdProp { get; set; } + + public InnerController(ApexController parent) { + this.parent = parent; + this.InnerAccountIdProp = parent.AccountIdProp; + } + + public Id getInnerAccountId() { + return 'Inner: ' + parent.acc.id; + } + + public String getInnerAccountName() { + return 'Inner: ' + parent.acc.name; + } + } +} \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-mdapi/objects/Account.object b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/objects/Account.object similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-mdapi/objects/Account.object rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/objects/Account.object diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page new file mode 100644 index 0000000000..d1e8d3fb88 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page @@ -0,0 +1 @@ + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Checkbox__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Checkbox__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/DateTime__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/DateTime__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Picklist__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Picklist__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/TextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/TextArea__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Text__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/objects/Account/fields/Text__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page new file mode 100644 index 0000000000..d1e8d3fb88 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page @@ -0,0 +1 @@ + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/objects/Account.object b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/objects/Account.object new file mode 100644 index 0000000000..5c37ef3c93 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/objects/Account.object @@ -0,0 +1,445 @@ + + + + CallHighlightAction + Default + + + CallHighlightAction + Large + Default + + + CallHighlightAction + Small + Default + + + CancelEdit + Default + + + CancelEdit + Large + Default + + + CancelEdit + Small + Default + + + Delete + Default + + + Delete + Large + Default + + + Delete + Small + Default + + + Edit + Default + + + Edit + Large + Default + + + Edit + Small + Default + + + EmailHighlightAction + Default + + + EmailHighlightAction + Large + Default + + + EmailHighlightAction + Small + Default + + + EnableCustomerPortalUser + Default + + + EnableCustomerPortalUser + Large + Default + + + EnableCustomerPortalUser + Small + Default + + + List + Default + + + List + Large + Default + + + List + Small + Default + + + ListClean + Default + + + ListClean + Large + Default + + + ListClean + Small + Default + + + New + Default + + + New + Large + Default + + + New + Small + Default + + + RequestUpdate + Default + + + RequestUpdate + Large + Default + + + RequestUpdate + Small + Default + + + SaveEdit + Default + + + SaveEdit + Large + Default + + + SaveEdit + Small + Default + + + SmsHighlightAction + Default + + + SmsHighlightAction + Large + Default + + + SmsHighlightAction + Small + Default + + + Tab + Default + + + Tab + Large + Default + + + Tab + Small + Default + + + View + Default + + + View + Large + Default + + + View + Small + Default + + + ViewCustomerPortalUser + Default + + + ViewCustomerPortalUser + Large + Default + + + ViewCustomerPortalUser + Small + Default + + + WebsiteHighlightAction + Default + + + WebsiteHighlightAction + Large + Default + + + WebsiteHighlightAction + Small + Default + + SYSTEM + true + false + Private + + ACCOUNT.NAME + ACCOUNT.ADDRESS1_CITY + ACCOUNT.PHONE1 + ACCOUNT.NAME + ACCOUNT.SITE + CORE.USERS.ALIAS + ACCOUNT.TYPE + ACCOUNT.NAME + ACCOUNT.SITE + CORE.USERS.ALIAS + ACCOUNT.TYPE + ACCOUNT.PHONE1 + ACCOUNT.NAME + ACCOUNT.SITE + ACCOUNT.PHONE1 + CORE.USERS.ALIAS + + ReadWrite + + AccountNumber + false + + + AccountSource + false + Picklist + + + AnnualRevenue + false + + + BillingAddress + false + + + MDCheckbox__c + false + false + + false + Checkbox + + + CleanStatus + false + + + DandbCompanyId + false + Lookup + + + MDDateTime__c + false + + false + false + DateTime + + + Description + false + + + DunsNumber + false + + + Fax + false + + + Industry + false + Picklist + + + Jigsaw + false + + + NaicsCode + false + + + NaicsDesc + false + + + Name + true + + + NumberOfEmployees + false + + + OperatingHoursId + false + Lookup + + + OwnerId + true + Lookup + + + Ownership + false + Picklist + + + ParentId + false + Hierarchy + + + Phone + false + + + MDPicklist__c + false + + false + false + Picklist + + + false + + Value1 + false + + + + Value2 + false + + + + + + + Rating + false + Picklist + + + ShippingAddress + false + + + Sic + false + + + SicDesc + false + + + Site + false + + + MDLongTextArea__c + false + + 32768 + false + LongTextArea + 3 + + + MDTextArea__c + false + + false + false + TextArea + + + MDText__c + false + + 255 + false + false + Text + false + + + TickerSymbol + false + + + Tradestyle + false + + + Type + false + Picklist + + + Website + false + + + YearStarted + false + + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-mdapi/pages/StandardAccount.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/pages/StandardAccount.page similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-mdapi/pages/StandardAccount.page rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/pages/StandardAccount.page diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexController.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexController.cls new file mode 100644 index 0000000000..df449e3e6f --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexController.cls @@ -0,0 +1,39 @@ +public class ApexController { + public Id AccountIdProp { get; set; } + + public ApexController() { + acc = [SELECT Id, Name, Site FROM Account + WHERE Id = :ApexPages.currentPage().getParameters().get('id')]; + this.AccountIdProp = acc.Id; + } + + public Id getAccountId() { + return acc.id; + } + + public String getAccountName() { + return acc.name; + } + + public InnerController getInnerController() { + return new InnerController(this); + } + + public class InnerController { + private ApexController parent; + public Id InnerAccountIdProp { get; set; } + + public InnerController(ApexController parent) { + this.parent = parent; + this.InnerAccountIdProp = parent.AccountIdProp; + } + + public Id getInnerAccountId() { + return 'Inner: ' + parent.acc.id; + } + + public String getInnerAccountName() { + return 'Inner: ' + parent.acc.name; + } + } +} \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexExtension1.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexExtension1.cls similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexExtension1.cls rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexExtension1.cls diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexExtension2.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexExtension2.cls similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/classes/ApexExtension2.cls rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/classes/ApexExtension2.cls diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml new file mode 100644 index 0000000000..03ffc46229 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml @@ -0,0 +1,9 @@ + + + Checkbox__c + false + false + + false + Checkbox + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml new file mode 100644 index 0000000000..5e0ce1cd2c --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml @@ -0,0 +1,9 @@ + + + DateTime__c + false + + false + false + DateTime + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml new file mode 100644 index 0000000000..6de4650b1f --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml @@ -0,0 +1,10 @@ + + + LongTextArea__c + false + + 32768 + false + LongTextArea + 3 + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml new file mode 100644 index 0000000000..597d9d3d30 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml @@ -0,0 +1,24 @@ + + + Picklist__c + false + + false + false + Picklist + + + false + + Value1 + false + + + + Value2 + false + + + + + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml new file mode 100644 index 0000000000..cd59083b9e --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml @@ -0,0 +1,9 @@ + + + TextArea__c + false + + false + false + TextArea + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml new file mode 100644 index 0000000000..efeeb5262a --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Text__c + false + + 255 + false + false + Text + false + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/ApexController.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/ApexController.page similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/ApexController.page rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/ApexController.page diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/StandardAccount.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccount.page similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/StandardAccount.page rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccount.page diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/StandardAccountWithExtensions.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccountWithExtensions.page similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/metadata-sfdx/pages/StandardAccountWithExtensions.page rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccountWithExtensions.page From e1c42a10ec32403cc1ccae82bce4c420694f3e2c Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Thu, 12 Nov 2020 20:13:19 -0800 Subject: [PATCH 29/98] Move visitor to VfParser#parse LanguageVersionHandler#getTypeResolutionFacade is deprecated. Moved the VfExpressionTypeVisitor creation and execution to VfParser#parse instead. ParsingOptionsTest located in pmd-test wasn't running previously because it was in the src/main hierarchy. Moved this test into the src/test hierarchy and consolidated the methods from the similarly named class from pmd-core. --- .../EcmascriptParserOptionsTest.java | 2 +- ...sTest.java => ParserOptionsTestUtils.java} | 33 +---- .../pmd/lang/ParserOptionsTest.java | 113 +++++++++++++----- .../pmd/lang/vf/VfExpressionTypeVisitor.java | 4 + .../sourceforge/pmd/lang/vf/VfHandler.java | 13 -- .../net/sourceforge/pmd/lang/vf/VfParser.java | 4 +- .../pmd/lang/vf/ast/ASTCompilationUnit.java | 16 --- .../vf/rule/security/VfUnescapeElRule.java | 4 - .../pmd/lang/xml/XmlParserOptionsTest.java | 2 +- 9 files changed, 97 insertions(+), 94 deletions(-) rename pmd-test/src/main/java/net/sourceforge/pmd/lang/{ParserOptionsTest.java => ParserOptionsTestUtils.java} (69%) rename {pmd-core => pmd-test}/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java (53%) diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptParserOptionsTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptParserOptionsTest.java index da18981777..1dce10abb0 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptParserOptionsTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptParserOptionsTest.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.ecmascript; -import static net.sourceforge.pmd.lang.ParserOptionsTest.verifyOptionsEqualsHashcode; +import static net.sourceforge.pmd.lang.ParserOptionsTestUtils.verifyOptionsEqualsHashcode; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTestUtils.java similarity index 69% rename from pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java rename to pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTestUtils.java index 945946df05..cab491c5e8 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTestUtils.java @@ -5,38 +5,9 @@ package net.sourceforge.pmd.lang; import org.junit.Assert; -import org.junit.Test; -/** - * Unit tests for {@link ParserOptions}. - */ -public class ParserOptionsTest { - - /** - * SuppressMarker should be initially null and changeable. - */ - @Test - public void testSuppressMarker() { - ParserOptions parserOptions = new ParserOptions(); - Assert.assertNull(parserOptions.getSuppressMarker()); - parserOptions.setSuppressMarker("foo"); - Assert.assertEquals("foo", parserOptions.getSuppressMarker()); - } - - /** - * Verify that the equals and hashCode methods work as expected. - */ - @Test - public void testEqualsHashcode() { - ParserOptions options1 = new ParserOptions(); - options1.setSuppressMarker("foo"); - ParserOptions options2 = new ParserOptions(); - options2.setSuppressMarker("bar"); - ParserOptions options3 = new ParserOptions(); - options3.setSuppressMarker("foo"); - ParserOptions options4 = new ParserOptions(); - options4.setSuppressMarker("bar"); - verifyOptionsEqualsHashcode(options1, options2, options3, options4); +public final class ParserOptionsTestUtils { + private ParserOptionsTestUtils() { } /** diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java b/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java similarity index 53% rename from pmd-core/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java rename to pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java index 1e5e2fe1b8..7f330d334a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java +++ b/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java @@ -6,20 +6,24 @@ package net.sourceforge.pmd.lang; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.commons.lang3.StringUtils; +import org.junit.Assert; import org.junit.Test; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; +import net.sourceforge.pmd.test.lang.DummyLanguageModule; +/** + * Unit tests for {@link ParserOptions}. + * This class is located in the pmd-test project instead of pmd-core so that it can invoke + * {@link ParserOptionsTestUtils#verifyOptionsEqualsHashcode} + */ public class ParserOptionsTest { private static final List DEFAULT_LIST = Arrays.asList("value1", "value2"); private static final String DEFAULT_STRING = "value3"; @@ -48,6 +52,17 @@ public class ParserOptionsTest { } } + /** + * SuppressMarker should be initially null and changeable. + */ + @Test + public void testSuppressMarker() { + ParserOptions parserOptions = new ParserOptions(); + Assert.assertNull(parserOptions.getSuppressMarker()); + parserOptions.setSuppressMarker("foo"); + Assert.assertEquals("foo", parserOptions.getSuppressMarker()); + } + @Test public void testDefaultPropertyDescriptors() { TestParserOptions parserOptions = new TestParserOptions(); @@ -104,37 +119,81 @@ public class ParserOptionsTest { assertEquals("", vfParserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); } + /** + * Verify that the equals and hashCode methods work as expected. + * TODO: Consider using Guava's EqualsTester + */ @Test - public void testEqualsAndHashCode() { - ParserOptions parserOptions = new ParserOptions(); - TestParserOptions testParserOptions1 = new TestParserOptions(); - TestParserOptions testParserOptions2 = new TestParserOptions(); + public void testSuppressMarkerEqualsHashCode() { + ParserOptions options1; + ParserOptions options2; + ParserOptions options3; + ParserOptions options4; - // Differences based on Language - assertNotNull(parserOptions.hashCode()); - assertFalse(parserOptions.equals(testParserOptions1)); - assertNotEquals(parserOptions.hashCode(), testParserOptions1.hashCode()); + // SuppressMarker + options1 = new ParserOptions(); + options2 = new ParserOptions(); + options3 = new ParserOptions(); + options4 = new ParserOptions(); + options1.setSuppressMarker("foo"); + options2.setSuppressMarker("bar"); + options3.setSuppressMarker("foo"); + options4.setSuppressMarker("bar"); + ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); - // Differences based on Properties - assertNotNull(testParserOptions1.hashCode()); - assertTrue(testParserOptions1.equals(testParserOptions2)); - assertEquals(testParserOptions1.hashCode(), testParserOptions2.hashCode()); + // PropertyDescriptor + options1 = new ParserOptions(); + options2 = new ParserOptions(); + options3 = new ParserOptions(); + options4 = new ParserOptions(); + options1.definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); + options2.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options3.definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); + options4.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); - testParserOptions1.setProperty(TestParserOptions.LIST_DESCRIPTOR, OVERRIDDEN_LIST); - assertFalse(testParserOptions1.equals(testParserOptions2)); - assertNotEquals(testParserOptions1.hashCode(), testParserOptions2.hashCode()); + // PropertyValue + options1 = new ParserOptions(); + options2 = new ParserOptions(); + options3 = new ParserOptions(); + options4 = new ParserOptions(); + options1.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options1.setProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); + options2.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options2.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + options3.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options3.setProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); + options4.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options4.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); - testParserOptions1.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); - assertFalse(testParserOptions1.equals(testParserOptions2)); - assertNotEquals(testParserOptions1.hashCode(), testParserOptions2.hashCode()); + // Language + options1 = new ParserOptions(new DummyLanguageModule()); + options2 = new ParserOptions(); + options3 = new ParserOptions(new DummyLanguageModule()); + options4 = new ParserOptions(); + ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); - testParserOptions2.setProperty(TestParserOptions.LIST_DESCRIPTOR, OVERRIDDEN_LIST); - assertFalse(testParserOptions1.equals(testParserOptions2)); - assertNotEquals(testParserOptions1.hashCode(), testParserOptions2.hashCode()); + // SuppressMarker, PropertyDescriptor, PropertyValue, Language + options1 = new ParserOptions(new DummyLanguageModule()); + options2 = new ParserOptions(); + options3 = new ParserOptions(new DummyLanguageModule()); + options4 = new ParserOptions(); + options1.setSuppressMarker("foo"); + options2.setSuppressMarker("bar"); + options3.setSuppressMarker("foo"); + options4.setSuppressMarker("bar"); + options1.definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); + options1.setProperty(TestParserOptions.LIST_DESCRIPTOR, DEFAULT_LIST); + options2.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options2.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + options3.definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); + options3.setProperty(TestParserOptions.LIST_DESCRIPTOR, DEFAULT_LIST); + options4.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options4.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); - testParserOptions2.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); - assertTrue(testParserOptions1.equals(testParserOptions2)); - assertEquals(testParserOptions1.hashCode(), testParserOptions2.hashCode()); + assertFalse(options1.equals(null)); } @Test diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java index f17e4cb5ef..5f3d984b82 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java @@ -86,6 +86,10 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { @Override public Object visit(ASTCompilationUnit node, Object data) { + if (apexDirectories.isEmpty() && objectsDirectories.isEmpty()) { + // Skip visiting if there aren't any directories to look in + return data; + } this.fileName = AbstractTokenManager.getFileName(); return super.visit(node, data); } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java index e7bfc2c2fe..31fd287511 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java @@ -12,7 +12,6 @@ import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.VisitorStarter; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; -import net.sourceforge.pmd.lang.vf.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.vf.ast.DumpFacade; import net.sourceforge.pmd.lang.vf.ast.VfNode; import net.sourceforge.pmd.lang.vf.rule.VfRuleViolationFactory; @@ -29,18 +28,6 @@ public class VfHandler extends AbstractLanguageVersionHandler { return new VfParser(parserOptions); } - @Override - public VisitorStarter getTypeResolutionFacade(ClassLoader classLoader) { - return new VisitorStarter() { - @Override - public void start(Node rootNode) { - ASTCompilationUnit astCompilationUnit = (ASTCompilationUnit) rootNode; - VfExpressionTypeVisitor visitor = new VfExpressionTypeVisitor(astCompilationUnit.getPropertySource()); - visitor.visit((ASTCompilationUnit) rootNode, null); - } - }; - } - @Override public ParserOptions getDefaultParserOptions() { return new VfParserOptions(); diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java index 0c14ab733f..894967370c 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java @@ -46,7 +46,9 @@ public class VfParser extends AbstractParser { AbstractTokenManager.setFileName(fileName); ASTCompilationUnit astCompilationUnit = new net.sourceforge.pmd.lang.vf.ast.VfParser( new SimpleCharStream(source)).CompilationUnit(); - astCompilationUnit.setPropertySource(this.getParserOptions()); + // Add type information to the AST + VfExpressionTypeVisitor visitor = new VfExpressionTypeVisitor(this.getParserOptions()); + visitor.visit(astCompilationUnit, null); return astCompilationUnit; } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTCompilationUnit.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTCompilationUnit.java index 2d57e0db00..65827db0a5 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTCompilationUnit.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTCompilationUnit.java @@ -6,16 +6,8 @@ package net.sourceforge.pmd.lang.vf.ast; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.RootNode; -import net.sourceforge.pmd.properties.PropertySource; public class ASTCompilationUnit extends AbstractVFNode implements RootNode { - /** - * Holds the properties contained in {@code VfParserOptions}. This class acts as an intermediary since it is - * accessible by {@code VFParser} and {@code VFHandler}. {@code VfHandler} uses this value to initialize the - * {@code VfExpressionTypeVisitor} for type resolution. - */ - private PropertySource propertySource; - @Deprecated @InternalApi public ASTCompilationUnit(int id) { @@ -32,12 +24,4 @@ public class ASTCompilationUnit extends AbstractVFNode implements RootNode { public Object jjtAccept(VfParserVisitor visitor, Object data) { return visitor.visit(this, data); } - - public PropertySource getPropertySource() { - return propertySource; - } - - public void setPropertySource(PropertySource propertySource) { - this.propertySource = propertySource; - } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java index ccda6bb4d6..601a83323e 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java @@ -51,10 +51,6 @@ public class VfUnescapeElRule extends AbstractVfRule { private static final Pattern ON_EVENT = Pattern.compile("^on(\\w)+$"); private static final Pattern PLACEHOLDERS = Pattern.compile("\\{(\\w|,|\\.|'|:|\\s)*\\}"); - public VfUnescapeElRule() { - this.setTypeResolution(true); - } - @Override public Object visit(ASTHtmlScript node, Object data) { checkIfCorrectlyEscaped(node, data); diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserOptionsTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserOptionsTest.java index f9c9f2529c..b7bec79b7b 100644 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserOptionsTest.java +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserOptionsTest.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.xml; -import static net.sourceforge.pmd.lang.ParserOptionsTest.verifyOptionsEqualsHashcode; +import static net.sourceforge.pmd.lang.ParserOptionsTestUtils.verifyOptionsEqualsHashcode; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; From 87892adefe4b6dfe9055ea9f04a5ef98ff6b30d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Nov 2020 17:23:43 +0100 Subject: [PATCH 30/98] Fix unnecessary throws clause --- .../src/main/java/net/sourceforge/pmd/RuleSetFactory.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java index 8cf19d28d5..efd2b0ab31 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -421,9 +421,6 @@ public class RuleSetFactory { ruleSetBuilder.filterRulesByPriority(minimumPriority); return ruleSetBuilder.build(); - } catch (ReflectiveOperationException ex) { - ex.printStackTrace(); - throw new RuntimeException("Couldn't find the class " + ex.getMessage(), ex); } catch (ParserConfigurationException | IOException | SAXException ex) { ex.printStackTrace(); throw new RuntimeException("Couldn't read the ruleset " + ruleSetReferenceId + ": " + ex.getMessage(), ex); @@ -495,7 +492,7 @@ public class RuleSetFactory { */ private void parseRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, Node ruleNode, boolean withDeprecatedRuleReferences, Set rulesetReferences) - throws ClassNotFoundException, InstantiationException, IllegalAccessException, RuleSetNotFoundException { + throws RuleSetNotFoundException { Element ruleElement = (Element) ruleNode; String ref = ruleElement.getAttribute("ref"); if (ref.endsWith("xml")) { @@ -603,7 +600,7 @@ public class RuleSetFactory { * Must be a rule element node. */ private void parseSingleRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, - Node ruleNode) throws ClassNotFoundException, InstantiationException, IllegalAccessException { + Node ruleNode) { Element ruleElement = (Element) ruleNode; // Stop if we're looking for a particular Rule, and this element is not From 86b5948f072a789d4a95b7934c0b0814021088e2 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Thu, 19 Nov 2020 13:48:01 -0800 Subject: [PATCH 31/98] Update the way nodes with data are identified Changed method for how the Visualforce strings are reconstructed from the AST. The previous implementation had incorrect assumptions about the structure of the AST. Added tests to more thoroughly test these situations. Changed name of IdentifierType to DataType. This information can be stored on either ASTIdentifier or ASTLiteral nodes. Changes based on PR feedgack: - Restored ParserOptionsTest in order to avoid binary compatibilty issues. - Changed ParserOptions to contain a PropertySource instead of extending AbtractPropertySource. --- .../sourceforge/pmd/lang/ParserOptions.java | 44 ++-- .../pmd/lang/ParserOptionsTest.java | 61 +++++ ...nsTest.java => ParserOptionsUnitTest.java} | 71 +++--- .../pmd/lang/vf/ApexClassPropertyTypes.java | 99 +++----- .../vf/ApexClassPropertyTypesVisitor.java | 2 +- .../vf/{IdentifierType.java => DataType.java} | 50 ++-- .../pmd/lang/vf/ObjectFieldTypes.java | 160 +++++------- .../pmd/lang/vf/SalesforceFieldTypes.java | 99 ++++++++ .../pmd/lang/vf/VfExpressionTypeVisitor.java | 105 +++----- .../net/sourceforge/pmd/lang/vf/VfParser.java | 3 +- .../pmd/lang/vf/VfParserOptions.java | 33 ++- .../pmd/lang/vf/ast/ASTExpression.java | 134 ++++++++++ .../pmd/lang/vf/ast/ASTIdentifier.java | 15 +- .../pmd/lang/vf/ast/ASTLiteral.java | 2 +- .../pmd/lang/vf/ast/AbstractVFDataNode.java | 41 +++ .../vf/rule/security/VfUnescapeElRule.java | 31 ++- .../lang/vf/ApexClassPropertyTypesTest.java | 89 +++---- .../sourceforge/pmd/lang/vf/DataTypeTest.java | 38 +++ .../pmd/lang/vf/IdentifierTypeTest.java | 38 --- .../pmd/lang/vf/ObjectFieldTypesTest.java | 69 +++-- .../sourceforge/pmd/lang/vf/VFTestUtils.java | 41 +++ .../lang/vf/VfExpressionTypeVisitorTest.java | 187 ++++++++++++++ .../pmd/lang/vf/VfParserOptionsTest.java | 8 +- .../pmd/lang/vf/ast/ASTExpressionTest.java | 237 ++++++++++++++++++ .../vf/rule/security/VfUnescapeElTest.java | 20 +- .../metadata/sfdx/classes/ApexController.cls | 5 + .../ApexWithConflictingPropertyTypes.cls | 7 - .../metadata/sfdx/pages/SomePage.page | 4 +- .../metadata/mdapi/pages/SomePage.page | 4 +- .../metadata/sfdx/pages/SomePage.page | 4 +- .../metadata/sfdx/classes/ApexController.cls | 44 ++++ .../Account/fields/Checkbox__c.field-meta.xml | 9 + .../Account/fields/DateTime__c.field-meta.xml | 9 + .../fields/LongTextArea__c.field-meta.xml | 10 + .../Account/fields/Picklist__c.field-meta.xml | 24 ++ .../Account/fields/TextArea__c.field-meta.xml | 9 + .../Account/fields/Text__c.field-meta.xml | 11 + .../metadata/sfdx/pages/ApexController.page | 18 ++ .../metadata/sfdx/pages/StandardAccount.page | 41 +++ .../metadata/mdapi/pages/StandardAccount.page | 2 +- .../metadata/sfdx/pages/StandardAccount.page | 22 +- .../pages/StandardAccountWithExtensions.page | 2 +- 42 files changed, 1421 insertions(+), 481 deletions(-) create mode 100644 pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java rename pmd-test/src/test/java/net/sourceforge/pmd/lang/{ParserOptionsTest.java => ParserOptionsUnitTest.java} (68%) rename pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/{IdentifierType.java => DataType.java} (61%) create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/SalesforceFieldTypes.java create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/DataTypeTest.java delete mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/IdentifierTypeTest.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitorTest.java create mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ASTExpressionTest.java delete mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexWithConflictingPropertyTypes.cls create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/classes/ApexController.cls create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/pages/ApexController.page create mode 100644 pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/pages/StandardAccount.page diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java index fb983474eb..cd04097ac3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java @@ -9,6 +9,7 @@ import java.util.Objects; import net.sourceforge.pmd.properties.AbstractPropertySource; import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertySource; /** * Represents a set of configuration options for a {@link Parser}. For each @@ -16,7 +17,7 @@ import net.sourceforge.pmd.properties.PropertyDescriptor; * Therefore, implementations must implement {@link Object#equals(Object)} and * {@link Object#hashCode()}. */ -public class ParserOptions extends AbstractPropertySource { +public class ParserOptions { /** * @deprecated Use {@link #getSuppressMarker()} instead. */ @@ -28,12 +29,15 @@ public class ParserOptions extends AbstractPropertySource { */ private final Language language; + private final ParserOptionsProperties parserOptionsProperties; + public ParserOptions() { this(null); } public ParserOptions(Language language) { this.language = language; + this.parserOptionsProperties = new ParserOptionsProperties(); } public String getSuppressMarker() { @@ -44,6 +48,10 @@ public class ParserOptions extends AbstractPropertySource { this.suppressMarker = suppressMarker; } + public PropertySource getProperties() { + return parserOptionsProperties; + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -52,27 +60,15 @@ public class ParserOptions extends AbstractPropertySource { if (obj == null || getClass() != obj.getClass()) { return false; } - if (!super.equals(obj)) { - return false; - } final ParserOptions that = (ParserOptions) obj; return Objects.equals(suppressMarker, that.suppressMarker) - && Objects.equals(language, that.language); + && Objects.equals(language, that.language) + && Objects.equals(parserOptionsProperties, that.parserOptionsProperties); } @Override public int hashCode() { - return Objects.hash(super.hashCode(), suppressMarker, language); - } - - @Override - protected String getPropertySourceType() { - return "ParserOptions"; - } - - @Override - public String getName() { - return getClass().getSimpleName(); + return Objects.hash(suppressMarker, language, parserOptionsProperties); } /** @@ -99,13 +95,25 @@ public class ParserOptions extends AbstractPropertySource { * TODO: Move this to net.sourceforge.pmd.PMD#parserFor when CLI options are implemented */ protected void overridePropertiesFromEnv() { - for (PropertyDescriptor propertyDescriptor : getPropertyDescriptors()) { + for (PropertyDescriptor propertyDescriptor : parserOptionsProperties.getPropertyDescriptors()) { String propertyValue = getEnvValue(propertyDescriptor); if (propertyValue != null) { Object value = propertyDescriptor.valueFrom(propertyValue); - setProperty(propertyDescriptor, value); + parserOptionsProperties.setProperty(propertyDescriptor, value); } } } + + private final class ParserOptionsProperties extends AbstractPropertySource { + @Override + protected String getPropertySourceType() { + return "ParserOptions"; + } + + @Override + public String getName() { + return ParserOptions.this.getClass().getSimpleName(); + } + } } diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java new file mode 100644 index 0000000000..f5127614e1 --- /dev/null +++ b/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java @@ -0,0 +1,61 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Unit tests for {@link ParserOptions}. + * @deprecated for removal in PMD 7.0. Use {@link ParserOptionsTestUtils}. + */ +@Deprecated +public class ParserOptionsTest { + + /** + * SuppressMarker should be initially null and changeable. + */ + @Test + public void testSuppressMarker() { + ParserOptions parserOptions = new ParserOptions(); + Assert.assertNull(parserOptions.getSuppressMarker()); + parserOptions.setSuppressMarker("foo"); + Assert.assertEquals("foo", parserOptions.getSuppressMarker()); + } + + /** + * Verify that the equals and hashCode methods work as expected. + */ + @Test + public void testEqualsHashcode() { + ParserOptions options1 = new ParserOptions(); + options1.setSuppressMarker("foo"); + ParserOptions options2 = new ParserOptions(); + options2.setSuppressMarker("bar"); + ParserOptions options3 = new ParserOptions(); + options3.setSuppressMarker("foo"); + ParserOptions options4 = new ParserOptions(); + options4.setSuppressMarker("bar"); + verifyOptionsEqualsHashcode(options1, options2, options3, options4); + } + + /** + * Verify equals and hashCode for 4 {@link ParserOptions} instances. The + * given options should be as follows: 1 and 3 are equals, as are 2 and 4. + * + * @param options1 + * first option instance - equals third + * @param options2 + * second option instance - equals fourth + * @param options3 + * third option instance - equals first + * @param options4 + * fourth option instance - equals second + */ + public static void verifyOptionsEqualsHashcode(ParserOptions options1, ParserOptions options2, + ParserOptions options3, ParserOptions options4) { + ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); + } +} diff --git a/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java b/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java similarity index 68% rename from pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java rename to pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java index 7f330d334a..e4b45cc817 100644 --- a/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsTest.java +++ b/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java @@ -23,8 +23,10 @@ import net.sourceforge.pmd.test.lang.DummyLanguageModule; * Unit tests for {@link ParserOptions}. * This class is located in the pmd-test project instead of pmd-core so that it can invoke * {@link ParserOptionsTestUtils#verifyOptionsEqualsHashcode} + * + * TODO: 7.0.0: Rename to ParserOptionsTest when {@link ParserOptionsTest} is removed. */ -public class ParserOptionsTest { +public class ParserOptionsUnitTest { private static final List DEFAULT_LIST = Arrays.asList("value1", "value2"); private static final String DEFAULT_STRING = "value3"; private static final List OVERRIDDEN_LIST = Arrays.asList("override1", "override2"); @@ -46,8 +48,8 @@ public class ParserOptionsTest { private TestParserOptions() { super(new DummyLanguageModule()); - definePropertyDescriptor(LIST_DESCRIPTOR); - definePropertyDescriptor(STRING_DESCRIPTOR); + getProperties().definePropertyDescriptor(LIST_DESCRIPTOR); + getProperties().definePropertyDescriptor(STRING_DESCRIPTOR); overridePropertiesFromEnv(); } } @@ -66,20 +68,19 @@ public class ParserOptionsTest { @Test public void testDefaultPropertyDescriptors() { TestParserOptions parserOptions = new TestParserOptions(); - assertEquals(DEFAULT_LIST, parserOptions.getProperty(TestParserOptions.LIST_DESCRIPTOR)); - assertEquals(DEFAULT_STRING, parserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); - assertEquals("ParserOptions", parserOptions.getPropertySourceType()); - assertEquals("TestParserOptions", parserOptions.getName()); + assertEquals(DEFAULT_LIST, parserOptions.getProperties().getProperty(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals(DEFAULT_STRING, parserOptions.getProperties().getProperty(TestParserOptions.STRING_DESCRIPTOR)); + assertEquals("TestParserOptions", parserOptions.getProperties().getName()); } @Test public void testOverriddenPropertyDescriptors() { TestParserOptions parserOptions = new TestParserOptions(); - parserOptions.setProperty(TestParserOptions.LIST_DESCRIPTOR, OVERRIDDEN_LIST); - parserOptions.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + parserOptions.getProperties().setProperty(TestParserOptions.LIST_DESCRIPTOR, OVERRIDDEN_LIST); + parserOptions.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); - assertEquals(OVERRIDDEN_LIST, parserOptions.getProperty(TestParserOptions.LIST_DESCRIPTOR)); - assertEquals(OVERRIDDEN_STRING, parserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); + assertEquals(OVERRIDDEN_LIST, parserOptions.getProperties().getProperty(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals(OVERRIDDEN_STRING, parserOptions.getProperties().getProperty(TestParserOptions.STRING_DESCRIPTOR)); } @Test @@ -97,8 +98,8 @@ public class ParserOptionsTest { } }; - assertEquals(OVERRIDDEN_LIST, parserOptions.getProperty(TestParserOptions.LIST_DESCRIPTOR)); - assertEquals(OVERRIDDEN_STRING, parserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); + assertEquals(OVERRIDDEN_LIST, parserOptions.getProperties().getProperty(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals(OVERRIDDEN_STRING, parserOptions.getProperties().getProperty(TestParserOptions.STRING_DESCRIPTOR)); } @Test @@ -115,8 +116,8 @@ public class ParserOptionsTest { } }; - assertEquals(Collections.emptyList(), vfParserOptions.getProperty(TestParserOptions.LIST_DESCRIPTOR)); - assertEquals("", vfParserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); + assertEquals(Collections.emptyList(), vfParserOptions.getProperties().getProperty(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals("", vfParserOptions.getProperties().getProperty(TestParserOptions.STRING_DESCRIPTOR)); } /** @@ -146,10 +147,10 @@ public class ParserOptionsTest { options2 = new ParserOptions(); options3 = new ParserOptions(); options4 = new ParserOptions(); - options1.definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); - options2.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options3.definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); - options4.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options1.getProperties().definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); + options2.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options3.getProperties().definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); + options4.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); // PropertyValue @@ -157,14 +158,14 @@ public class ParserOptionsTest { options2 = new ParserOptions(); options3 = new ParserOptions(); options4 = new ParserOptions(); - options1.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options1.setProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); - options2.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options2.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); - options3.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options3.setProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); - options4.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options4.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + options1.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options1.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); + options2.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options2.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + options3.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options3.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); + options4.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options4.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); // Language @@ -183,14 +184,14 @@ public class ParserOptionsTest { options2.setSuppressMarker("bar"); options3.setSuppressMarker("foo"); options4.setSuppressMarker("bar"); - options1.definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); - options1.setProperty(TestParserOptions.LIST_DESCRIPTOR, DEFAULT_LIST); - options2.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options2.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); - options3.definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); - options3.setProperty(TestParserOptions.LIST_DESCRIPTOR, DEFAULT_LIST); - options4.definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options4.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + options1.getProperties().definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); + options1.getProperties().setProperty(TestParserOptions.LIST_DESCRIPTOR, DEFAULT_LIST); + options2.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options2.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + options3.getProperties().definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); + options3.getProperties().setProperty(TestParserOptions.LIST_DESCRIPTOR, DEFAULT_LIST); + options4.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options4.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); assertFalse(options1.equals(null)); diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java index d44a6f2571..2de3e5c277 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes.java @@ -9,15 +9,10 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; import java.util.logging.Logger; +import org.apache.commons.lang3.exception.ContextedRuntimeException; import org.apache.commons.lang3.tuple.Pair; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -34,93 +29,65 @@ import apex.jorje.semantic.symbol.type.BasicType; * Responsible for storing a mapping of Apex Class properties that can be referenced from Visualforce to the type of the * property. */ -class ApexClassPropertyTypes { +class ApexClassPropertyTypes extends SalesforceFieldTypes { private static final Logger LOGGER = Logger.getLogger(ApexClassPropertyTypes.class.getName()); private static final String APEX_CLASS_FILE_SUFFIX = ".cls"; - private final Map variableNameToVariableType; - private final Set variableNameProcessed; - - ApexClassPropertyTypes() { - this.variableNameToVariableType = new HashMap<>(); - this.variableNameProcessed = new HashSet<>(); - } - /** * Looks in {@code apexDirectories} for an Apex property identified by {@code expression}. - * - * @return the IdentifierType for the property represented by {@code expression} or null if not found. */ - public IdentifierType getVariableType(String expression, String vfFileName, List apexDirectories) { - String lowerExpression = expression.toLowerCase(Locale.ROOT); - if (variableNameToVariableType.containsKey(lowerExpression)) { - // The expression has been previously retrieved - return variableNameToVariableType.get(lowerExpression); - } else if (variableNameProcessed.contains(lowerExpression)) { - // The expression has been previously requested, but was not found - return null; - } else { - String[] parts = expression.split("\\."); - if (parts.length >= 2) { - // Load the class and parse it - String className = parts[0]; + @Override + public void findDataType(String expression, List apexDirectories) { + String[] parts = expression.split("\\."); + if (parts.length >= 2) { + // Load the class and parse it + String className = parts[0]; - Path vfFilePath = Paths.get(vfFileName); - for (String apexDirectory : apexDirectories) { - Path candidateDirectory; - if (Paths.get(apexDirectory).isAbsolute()) { - candidateDirectory = Paths.get(apexDirectory); - } else { - candidateDirectory = vfFilePath.getParent().resolve(apexDirectory); + for (Path apexDirectory : apexDirectories) { + Path apexFilePath = apexDirectory.resolve(className + APEX_CLASS_FILE_SUFFIX); + if (Files.exists(apexFilePath) && Files.isRegularFile(apexFilePath)) { + Parser parser = getApexParser(); + try (BufferedReader reader = Files.newBufferedReader(apexFilePath, StandardCharsets.UTF_8)) { + Node node = parser.parse(apexFilePath.toString(), reader); + ApexClassPropertyTypesVisitor visitor = new ApexClassPropertyTypesVisitor(); + visitor.visit((ApexNode) node, null); + for (Pair variable : visitor.getVariables()) { + putDataType(variable.getKey(), DataType.fromBasicType(variable.getValue())); + } + } catch (IOException e) { + throw new ContextedRuntimeException(e) + .addContextValue("expression", expression) + .addContextValue("apexFilePath", apexFilePath); } - Path apexFilePath = candidateDirectory.resolve(className + APEX_CLASS_FILE_SUFFIX); - if (Files.exists(apexFilePath) && Files.isRegularFile(apexFilePath)) { - Parser parser = getApexParser(); - try (BufferedReader reader = Files.newBufferedReader(apexFilePath, StandardCharsets.UTF_8)) { - Node node = parser.parse(apexFilePath.toString(), reader); - ApexClassPropertyTypesVisitor visitor = new ApexClassPropertyTypesVisitor(); - visitor.visit((ApexNode) node, null); - for (Pair variable : visitor.getVariables()) { - setVariableType(variable.getKey(), variable.getValue()); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - - if (variableNameToVariableType.containsKey(lowerExpression)) { - // Break out of the loop if a variable was found - break; - } + if (containsExpression(expression)) { + // Break out of the loop if a variable was found + break; } } - variableNameProcessed.add(lowerExpression); - } else { - throw new RuntimeException("Malformed expression: " + expression); } } - - return variableNameToVariableType.get(lowerExpression); } - private void setVariableType(String name, BasicType basicType) { - IdentifierType identifierType = IdentifierType.fromBasicType(basicType); - IdentifierType previousType = variableNameToVariableType.put(name.toLowerCase(Locale.ROOT), identifierType); - if (previousType != null && !previousType.equals(identifierType)) { + @Override + protected DataType putDataType(String name, DataType dataType) { + DataType previousType = super.putDataType(name, dataType); + if (previousType != null && !previousType.equals(dataType)) { // It is possible to have a property and method with different types that appear the same to this code. An // example is an Apex class with a property "public String Foo {get; set;}" and a method of // "Integer getFoo() { return 1; }". In this case set the value as Unknown because we can't be sure which it // is. This code could be more complex in an attempt to determine if all the types are safe from escaping, // but we will allow a false positive in order to let the user know that the code could be refactored to be // more clear. - variableNameToVariableType.put(name.toLowerCase(Locale.ROOT), IdentifierType.Unknown); + super.putDataType(name, DataType.Unknown); LOGGER.warning("Conflicting types for " + name + ". CurrentType=" - + identifierType + + dataType + ", PreviousType=" + previousType); } + return previousType; } private Parser getApexParser() { diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor.java index cee166c5e5..79a109cc48 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor.java @@ -37,7 +37,7 @@ final class ApexClassPropertyTypesVisitor extends ApexParserVisitorAdapter { private static final String RETURN_TYPE_VOID = "void"; /** - * Pairs of (variableName, identifierType) + * Pairs of (variableName, BasicType) */ private final List> variables; diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/IdentifierType.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/DataType.java similarity index 61% rename from pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/IdentifierType.java rename to pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/DataType.java index ec95ced18b..2910ea11d4 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/IdentifierType.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/DataType.java @@ -20,7 +20,7 @@ import apex.jorje.semantic.symbol.type.BasicType; * * See https://developer.salesforce.com/docs/atlas.en-us.api_meta.meta/api_meta/meta_field_types.htm#meta_type_fieldtype */ -public enum IdentifierType { +public enum DataType { AutoNumber(false), Checkbox(false, BasicType.BOOLEAN), Currency(false, BasicType.CURRENCY), @@ -49,9 +49,13 @@ public enum IdentifierType { TextArea(true), Time(false, BasicType.TIME), Url(false), + /** + * Indicates that Metatada was found, but it's type was not mappable. This could because it is a type which isn't + * mapped, or it was an edge case where the type was ambiguously defined in the Metadata. + */ Unknown(true); - private static final Logger LOGGER = Logger.getLogger(IdentifierType.class.getName()); + private static final Logger LOGGER = Logger.getLogger(DataType.class.getName()); /** @@ -68,18 +72,18 @@ public enum IdentifierType { * A case insensitive map of the enum name to its instance. The case metadata is not guaranteed to have the correct * case. */ - private static final Map CASE_INSENSITIVE_MAP = new HashMap<>(); + private static final Map CASE_INSENSITIVE_MAP = new HashMap<>(); /** - * Map of BasicType to IdentifierType. Multiple BasicTypes may map to one ExrpessionType. + * Map of BasicType to DataType. Multiple BasicTypes may map to one DataType. */ - private static final Map BASIC_TYPE_MAP = new HashMap<>(); + private static final Map BASIC_TYPE_MAP = new HashMap<>(); static { - for (IdentifierType identifierType : IdentifierType.values()) { - CASE_INSENSITIVE_MAP.put(identifierType.name().toLowerCase(Locale.ROOT), identifierType); - for (BasicType basicType : identifierType.basicTypes) { - BASIC_TYPE_MAP.put(basicType, identifierType); + for (DataType dataType : DataType.values()) { + CASE_INSENSITIVE_MAP.put(dataType.name().toLowerCase(Locale.ROOT), dataType); + for (BasicType basicType : dataType.basicTypes) { + BASIC_TYPE_MAP.put(basicType, dataType); } } } @@ -87,37 +91,37 @@ public enum IdentifierType { /** * Map to correct instance, returns {@code Unknown} if the value can't be mapped. */ - public static IdentifierType fromString(String value) { + public static DataType fromString(String value) { value = value != null ? value : ""; - IdentifierType identifierType = CASE_INSENSITIVE_MAP.get(value.toLowerCase(Locale.ROOT)); + DataType dataType = CASE_INSENSITIVE_MAP.get(value.toLowerCase(Locale.ROOT)); - if (identifierType == null) { - identifierType = IdentifierType.Unknown; - LOGGER.fine("Unable to determine IdentifierType of " + value); + if (dataType == null) { + dataType = DataType.Unknown; + LOGGER.fine("Unable to determine DataType of " + value); } - return identifierType; + return dataType; } /** * Map to correct instance, returns {@code Unknown} if the value can't be mapped. */ - public static IdentifierType fromBasicType(BasicType value) { - IdentifierType identifierType = value != null ? BASIC_TYPE_MAP.get(value) : null; + public static DataType fromBasicType(BasicType value) { + DataType dataType = value != null ? BASIC_TYPE_MAP.get(value) : null; - if (identifierType == null) { - identifierType = IdentifierType.Unknown; - LOGGER.fine("Unable to determine IdentifierType of " + value); + if (dataType == null) { + dataType = DataType.Unknown; + LOGGER.fine("Unable to determine DataType of " + value); } - return identifierType; + return dataType; } - IdentifierType(boolean requiresEscaping) { + DataType(boolean requiresEscaping) { this(requiresEscaping, null); } - IdentifierType(boolean requiresEscaping, BasicType...basicTypes) { + DataType(boolean requiresEscaping, BasicType...basicTypes) { this.requiresEscaping = requiresEscaping; this.basicTypes = new HashSet<>(); if (basicTypes != null) { diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java index 6ff6f5cc27..8d32f45d84 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypes.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.vf; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -23,14 +24,16 @@ import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; +import org.apache.commons.lang3.exception.ContextedRuntimeException; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; /** * Responsible for storing a mapping of Fields that can be referenced from Visualforce to the type of the field. */ -class ObjectFieldTypes { +class ObjectFieldTypes extends SalesforceFieldTypes { private static final Logger LOGGER = Logger.getLogger(ObjectFieldTypes.class.getName()); public static final String CUSTOM_OBJECT_SUFFIX = "__c"; @@ -38,30 +41,20 @@ class ObjectFieldTypes { private static final String MDAPI_OBJECT_FILE_SUFFIX = ".object"; private static final String SFDX_FIELD_FILE_SUFFIX = ".field-meta.xml"; - private static final Map STANDARD_FIELD_TYPES; + private static final Map STANDARD_FIELD_TYPES; static { STANDARD_FIELD_TYPES = new HashMap<>(); - STANDARD_FIELD_TYPES.put("createdbyid", IdentifierType.Lookup); - STANDARD_FIELD_TYPES.put("createddate", IdentifierType.DateTime); - STANDARD_FIELD_TYPES.put("id", IdentifierType.Lookup); - STANDARD_FIELD_TYPES.put("isdeleted", IdentifierType.Checkbox); - STANDARD_FIELD_TYPES.put("lastmodifiedbyid", IdentifierType.Lookup); - STANDARD_FIELD_TYPES.put("lastmodifieddate", IdentifierType.DateTime); - STANDARD_FIELD_TYPES.put("systemmodstamp", IdentifierType.DateTime); + STANDARD_FIELD_TYPES.put("createdbyid", DataType.Lookup); + STANDARD_FIELD_TYPES.put("createddate", DataType.DateTime); + STANDARD_FIELD_TYPES.put("id", DataType.Lookup); + STANDARD_FIELD_TYPES.put("isdeleted", DataType.Checkbox); + STANDARD_FIELD_TYPES.put("lastmodifiedbyid", DataType.Lookup); + STANDARD_FIELD_TYPES.put("lastmodifieddate", DataType.DateTime); + STANDARD_FIELD_TYPES.put("name", DataType.Text); + STANDARD_FIELD_TYPES.put("systemmodstamp", DataType.DateTime); } - /** - * Cache of lowercase variable names to the variable type declared in the field's metadata file. - */ - private final Map variableNameToVariableType; - - /** - * Keep track of which variables were already processed. Avoid processing if a page repeatedly asks for an entry - * which we haven't previously found. - */ - private final Set variableNameProcessed; - /** * Keep track of which ".object" files have already been processed. All fields are processed at once. If an object * file has been processed @@ -77,8 +70,6 @@ class ObjectFieldTypes { private final XPathExpression sfdxCustomFieldTypeExpression; ObjectFieldTypes() { - this.variableNameToVariableType = new HashMap<>(); - this.variableNameProcessed = new HashSet<>(); this.objectFileProcessed = new HashSet<>(); try { @@ -111,68 +102,46 @@ class ObjectFieldTypes { /** * Looks in {@code objectsDirectories} for a custom field identified by {@code expression}. - * - * @return the IdentifierType for the field represented by {@code expression} or null the custom field isn't found. */ - public IdentifierType getVariableType(String expression, String vfFileName, List objectsDirectories) { - String lowerExpression = expression.toLowerCase(Locale.ROOT); + @Override + protected void findDataType(String expression, List objectsDirectories) { + // The expression should be in the form . + String[] parts = expression.split("\\."); + if (parts.length == 1) { + throw new RuntimeException("Malformed identifier: " + expression); + } else if (parts.length == 2) { + String objectName = parts[0]; + String fieldName = parts[1]; - if (variableNameToVariableType.containsKey(lowerExpression)) { - // The expression has been previously retrieved - return variableNameToVariableType.get(lowerExpression); - } else if (variableNameProcessed.contains(lowerExpression)) { - // The expression has been previously requested, but was not found - return null; - } else { - // The expression should be in the form . - String[] parts = expression.split("\\."); - if (parts.length == 1) { - throw new RuntimeException("Malformed identifier: " + expression); - } else if (parts.length == 2) { - String objectName = parts[0]; - String fieldName = parts[1]; + addStandardFields(objectName); - addStandardFields(objectName); - - // Attempt to find a metadata file that contains the custom field. The information will be located in a - // file located at /.object or in an file located at - // //fields/.field-meta.xml. The list of object directories - // defaults to the [/../objects] but can be overridden by the user. - Path vfFilePath = Paths.get(vfFileName); - for (String objectsDirectory : objectsDirectories) { - Path candidateDirectory; - if (Paths.get(objectsDirectory).isAbsolute()) { - candidateDirectory = Paths.get(objectsDirectory); - } else { - candidateDirectory = vfFilePath.getParent().resolve(objectsDirectory); - } - - Path sfdxCustomFieldPath = getSfdxCustomFieldPath(candidateDirectory, objectName, fieldName); - if (sfdxCustomFieldPath != null) { - // SFDX Format - parseSfdxCustomField(objectName, sfdxCustomFieldPath); - } else { - // MDAPI Format - String fileName = objectName + MDAPI_OBJECT_FILE_SUFFIX; - Path mdapiPath = candidateDirectory.resolve(fileName); - if (Files.exists(mdapiPath) && Files.isRegularFile(mdapiPath)) { - parseMdapiCustomObject(mdapiPath); - } - } - - if (variableNameToVariableType.containsKey(lowerExpression)) { - // Break out of the loop if a variable was found - break; + // Attempt to find a metadata file that contains the custom field. The information will be located in a + // file located at /.object or in an file located at + // //fields/.field-meta.xml. The list of object directories + // defaults to the [/../objects] but can be overridden by the user. + for (Path objectsDirectory : objectsDirectories) { + Path sfdxCustomFieldPath = getSfdxCustomFieldPath(objectsDirectory, objectName, fieldName); + if (sfdxCustomFieldPath != null) { + // SFDX Format + parseSfdxCustomField(objectName, sfdxCustomFieldPath); + } else { + // MDAPI Format + String fileName = objectName + MDAPI_OBJECT_FILE_SUFFIX; + Path mdapiPath = objectsDirectory.resolve(fileName); + if (Files.exists(mdapiPath) && Files.isRegularFile(mdapiPath)) { + parseMdapiCustomObject(mdapiPath); } } - variableNameProcessed.add(lowerExpression); - } else { - // TODO: Support cross object relationships, these are expressions that contain "__r" - LOGGER.fine("Expression does not have two parts: " + expression); - } - } - return variableNameToVariableType.get(lowerExpression); + if (containsExpression(expression)) { + // Break out of the loop if a variable was found + break; + } + } + } else { + // TODO: Support cross object relationships, these are expressions that contain "__r" + LOGGER.fine("Expression does not have two parts: " + expression); + } } /** @@ -201,12 +170,14 @@ class ObjectFieldTypes { Node fullNameNode = (Node) sfdxCustomFieldFullNameExpression.evaluate(document, XPathConstants.NODE); Node typeNode = (Node) sfdxCustomFieldTypeExpression.evaluate(document, XPathConstants.NODE); String type = typeNode.getNodeValue(); - IdentifierType identifierType = IdentifierType.fromString(type); + DataType dataType = DataType.fromString(type); String key = customObjectName + "." + fullNameNode.getNodeValue(); - setVariableType(key, identifierType); - } catch (Exception e) { - throw new RuntimeException(e); + putDataType(key, dataType); + } catch (IOException | SAXException | XPathExpressionException e) { + throw new ContextedRuntimeException(e) + .addContextValue("customObjectName", customObjectName) + .addContextValue("sfdxCustomFieldPath", sfdxCustomFieldPath); } } @@ -234,13 +205,15 @@ class ObjectFieldTypes { throw new RuntimeException("type evaluate failed for object=" + customObjectName + ", field=" + name + " " + fieldsNode.getTextContent()); } String type = typeNode.getNodeValue(); - IdentifierType identifierType = IdentifierType.fromString(type); + DataType dataType = DataType.fromString(type); String key = customObjectName + "." + fullNameNode.getNodeValue(); - setVariableType(key, identifierType); + putDataType(key, dataType); } } - } catch (Exception e) { - throw new RuntimeException(e); + } catch (IOException | SAXException | XPathExpressionException e) { + throw new ContextedRuntimeException(e) + .addContextValue("customObjectName", customObjectName) + .addContextValue("mdapiObjectFile", mdapiObjectFile); } objectFileProcessed.add(customObjectName); } @@ -251,8 +224,8 @@ class ObjectFieldTypes { * visualforce page. */ private void addStandardFields(String customObjectName) { - for (Map.Entry entry : STANDARD_FIELD_TYPES.entrySet()) { - setVariableType(customObjectName + "." + entry.getKey(), entry.getValue()); + for (Map.Entry entry : STANDARD_FIELD_TYPES.entrySet()) { + putDataType(customObjectName + "." + entry.getKey(), entry.getValue()); } } @@ -263,17 +236,18 @@ class ObjectFieldTypes { return str != null && str.toLowerCase(Locale.ROOT).endsWith(suffix.toLowerCase(Locale.ROOT)); } - private void setVariableType(String name, IdentifierType identifierType) { - name = name.toLowerCase(Locale.ROOT); - IdentifierType previousType = variableNameToVariableType.put(name, identifierType); - if (previousType != null && !previousType.equals(identifierType)) { + @Override + protected DataType putDataType(String name, DataType dataType) { + DataType previousType = super.putDataType(name, dataType); + if (previousType != null && !previousType.equals(dataType)) { // It should not be possible to have conflicting types for CustomFields throw new RuntimeException("Conflicting types for " + name + ". CurrentType=" - + identifierType + + dataType + ", PreviousType=" + previousType); } + return previousType; } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/SalesforceFieldTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/SalesforceFieldTypes.java new file mode 100644 index 0000000000..442ad5a983 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/SalesforceFieldTypes.java @@ -0,0 +1,99 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +/** + * Responsible for storing a mapping of Fields that can be referenced from Visualforce to the type of the field. The + * fields are identified by in a case insensitive manner. + */ +abstract class SalesforceFieldTypes { + /** + * Cache of lowercase variable names to the variable type declared in the field's metadata file. + */ + private final Map variableNameToVariableType; + + /** + * Keep track of which variables were already processed. Avoid processing if a page repeatedly asks for an entry + * which we haven't previously found. + */ + private final Set variableNameProcessed; + + SalesforceFieldTypes() { + this.variableNameToVariableType = new HashMap<>(); + this.variableNameProcessed = new HashSet<>(); + } + + /** + * + * @param expression expression literal as declared in the Visualforce page + * @param vfFileName file name of the Visualforce page that contains expression. Used to resolve relative paths + * included in {@code metadataDirectories} + * @param metadataDirectories absolute or relative list of directories that may contain the metadata corresponding + * to {@code expression} + * @return the DataType if it can be determined, else null + */ + public DataType getDataType(String expression, String vfFileName, List metadataDirectories) { + String lowerExpression = expression.toLowerCase(Locale.ROOT); + if (variableNameToVariableType.containsKey(lowerExpression)) { + // The expression has been previously retrieved + return variableNameToVariableType.get(lowerExpression); + } else if (variableNameProcessed.contains(lowerExpression)) { + // The expression has been previously requested, but was not found + return null; + } else { + Path vfFilePath = Paths.get(vfFileName); + List resolvedPaths = new ArrayList<>(); + for (String metadataDirectory : metadataDirectories) { + if (Paths.get(metadataDirectory).isAbsolute()) { + resolvedPaths.add(Paths.get(metadataDirectory)); + } else { + resolvedPaths.add(vfFilePath.getParent().resolve(metadataDirectory)); + } + } + + findDataType(expression, resolvedPaths); + variableNameProcessed.add(lowerExpression); + return variableNameToVariableType.get(lowerExpression); + } + } + + /** + * Stores {@link DataType} in a map using lower cased {@code expression} as the key. + * @param expression expression literal as declared in the Visualforce page + * @param dataType identifier determined for + * @return the previous value associated with {@code key}, or {@code null} if there was no mapping for {@code key}. + */ + protected DataType putDataType(String expression, DataType dataType) { + return variableNameToVariableType.put(expression.toLowerCase(Locale.ROOT), dataType); + } + + /** + * @return true if the expression has previously been stored via {@link #putDataType(String, DataType)} + */ + protected boolean containsExpression(String expression) { + return variableNameToVariableType.containsKey(expression.toLowerCase(Locale.ROOT)); + } + + /** + * Subclasses should attempt to find the {@code DataType} of {@code expression} within + * {@code metadataDirectories}. The subclass should store the value by invoking + * {@link #putDataType(String, DataType)}. + * + * @param expression expression as defined in the Visualforce page, case is preserved + * @param metadataDirectories list of directories that may contain the metadata corresponding to {@code expression} + */ + protected abstract void findDataType(String expression, List metadataDirectories); +} + diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java index 5f3d984b82..8c1bf42896 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java @@ -4,59 +4,34 @@ package net.sourceforge.pmd.lang.vf; -import java.io.File; import java.util.ArrayList; -import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.logging.Logger; -import net.sourceforge.pmd.lang.ast.AbstractTokenManager; +import org.apache.commons.lang3.StringUtils; + import net.sourceforge.pmd.lang.vf.ast.ASTAttribute; import net.sourceforge.pmd.lang.vf.ast.ASTAttributeValue; import net.sourceforge.pmd.lang.vf.ast.ASTCompilationUnit; -import net.sourceforge.pmd.lang.vf.ast.ASTDotExpression; import net.sourceforge.pmd.lang.vf.ast.ASTElExpression; import net.sourceforge.pmd.lang.vf.ast.ASTElement; import net.sourceforge.pmd.lang.vf.ast.ASTExpression; -import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; import net.sourceforge.pmd.lang.vf.ast.ASTText; +import net.sourceforge.pmd.lang.vf.ast.AbstractVFDataNode; import net.sourceforge.pmd.lang.vf.ast.VfParserVisitorAdapter; -import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertyFactory; import net.sourceforge.pmd.properties.PropertySource; /** - * Visits {@link ASTElExpression} nodes and stores type information for all {@link ASTIdentifier} child nodes. + * Visits {@link ASTExpression} nodes and stores type information for + * {@link net.sourceforge.pmd.lang.vf.ast.ASTIdentifier} children that represent an IdentifierDotted construct. An + * IdentifierDotted is of the form {@code MyObject__c.MyField__c}. */ -public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { +class VfExpressionTypeVisitor extends VfParserVisitorAdapter { private static final Logger LOGGER = Logger.getLogger(VfExpressionTypeVisitor.class.getName()); - static final List DEFAULT_APEX_DIRECTORIES = Collections.singletonList(".." + File.separator + "classes"); - static final List DEFAULT_OBJECT_DIRECTORIES = Collections.singletonList(".." + File.separator + "objects"); - - /** - * Directory that contains Apex classes that may be referenced from a Visualforce page. - */ - public static final PropertyDescriptor> APEX_DIRECTORIES_DESCRIPTOR = - PropertyFactory.stringListProperty("apexDirectories") - .desc("Location of Apex Class directories. Absolute or relative to the Visualforce directory.") - .defaultValue(DEFAULT_APEX_DIRECTORIES) - .delim(',') - .build(); - - /** - * Directory that contains Object definitions that may be referenced from a Visualforce page. - */ - public static final PropertyDescriptor> OBJECTS_DIRECTORIES_DESCRIPTOR = - PropertyFactory.stringListProperty("objectsDirectories") - .desc("Location of CustomObject directories. Absolute or relative to the Visualforce directory.") - .defaultValue(DEFAULT_OBJECT_DIRECTORIES) - .delim(',') - .build(); - private static final String APEX_PAGE = "apex:page"; private static final String CONTROLLER_ATTRIBUTE = "controller"; private static final String STANDARD_CONTROLLER_ATTRIBUTE = "standardcontroller"; @@ -64,8 +39,8 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { private final ApexClassPropertyTypes apexClassPropertyTypes; private final ObjectFieldTypes objectFieldTypes; + private final String fileName; - private String fileName; private String standardControllerName; /** @@ -76,9 +51,10 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { private final List apexDirectories; private final List objectsDirectories; - public VfExpressionTypeVisitor(PropertySource propertySource) { - this.apexDirectories = propertySource.getProperty(APEX_DIRECTORIES_DESCRIPTOR); - this.objectsDirectories = propertySource.getProperty(OBJECTS_DIRECTORIES_DESCRIPTOR); + VfExpressionTypeVisitor(String fileName, PropertySource propertySource) { + this.fileName = fileName; + this.apexDirectories = propertySource.getProperty(VfParserOptions.APEX_DIRECTORIES_DESCRIPTOR); + this.objectsDirectories = propertySource.getProperty(VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR); this.apexClassNames = new ArrayList<>(); this.apexClassPropertyTypes = new ApexClassPropertyTypes(); this.objectFieldTypes = new ObjectFieldTypes(); @@ -86,11 +62,15 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { @Override public Object visit(ASTCompilationUnit node, Object data) { + if (StringUtils.isBlank(fileName)) { + // Skip visiting if there isn't a file that can anchor the directories + return data; + } + if (apexDirectories.isEmpty() && objectsDirectories.isEmpty()) { // Skip visiting if there aren't any directories to look in return data; } - this.fileName = AbstractTokenManager.getFileName(); return super.visit(node, data); } @@ -125,14 +105,14 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { } /** - * Find all {@link ASTIdentifier} child nodes of {@code node} and attempt to resolve their type. The order of - * precedence is Controller, Extensions, StandardController. + * Invoke {@link ASTExpression#getDataNodes()} on all children of {@code node} and attempt to determine the + * {@link DataType} by looking at Apex or CustomField metadata. */ @Override public Object visit(ASTElExpression node, Object data) { - for (Map.Entry entry : getExpressionIdentifierNames(node).entrySet()) { + for (Map.Entry entry : getDataNodeNames(node).entrySet()) { String name = entry.getValue(); - IdentifierType type = null; + DataType type = null; String[] parts = name.split("\\."); // Apex extensions take precedence over Standard controllers. @@ -158,7 +138,7 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { // Try to find the identifier in an Apex class for (String apexClassName : apexClassNames) { String fullName = apexClassName + "." + name; - type = apexClassPropertyTypes.getVariableType(fullName, fileName, apexDirectories); + type = apexClassPropertyTypes.getDataType(fullName, fileName, apexDirectories); if (type != null) { break; } @@ -168,12 +148,12 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { // to the StandardController. if (type == null) { if (parts.length >= 2 && standardControllerName != null && standardControllerName.equalsIgnoreCase(parts[0])) { - type = objectFieldTypes.getVariableType(name, fileName, objectsDirectories); + type = objectFieldTypes.getDataType(name, fileName, objectsDirectories); } } if (type != null) { - entry.getKey().setIdentifierType(type); + entry.getKey().setDataType(type); } else { LOGGER.fine("Unable to determine type for: " + name); } @@ -182,36 +162,21 @@ public class VfExpressionTypeVisitor extends VfParserVisitorAdapter { } /** - * Parse the expression returning all of the identifiers in that expression mapped to its string represenation. - * An {@code ASTElExpression} can contain multiple {@code ASTExpressions} in cases of logical operators. + * Invoke {@link ASTExpression#getDataNodes()} for all {@link ASTExpression} children of {@code node} and return + * the consolidated results. */ - private Map getExpressionIdentifierNames(ASTElExpression elExpression) { - Map identifierToName = new IdentityHashMap<>(); + private IdentityHashMap getDataNodeNames(ASTElExpression node) { + IdentityHashMap dataNodeToName = new IdentityHashMap<>(); - for (ASTExpression expression : elExpression.findChildrenOfType(ASTExpression.class)) { - for (ASTIdentifier identifier : expression.findChildrenOfType(ASTIdentifier.class)) { - StringBuilder sb = new StringBuilder(identifier.getImage()); - - for (ASTDotExpression dotExpression : expression.findChildrenOfType(ASTDotExpression.class)) { - sb.append("."); - - List childIdentifiers = dotExpression.findChildrenOfType(ASTIdentifier.class); - if (childIdentifiers.isEmpty()) { - continue; - } else if (childIdentifiers.size() > 1) { - // The grammar guarantees tha there should be at most 1 child identifier - // (Identifier() | Literal() ) - throw new RuntimeException("Unexpected number of childIdentifiers: size=" + childIdentifiers.size()); - } - - ASTIdentifier childIdentifier = childIdentifiers.get(0); - sb.append(childIdentifier.getImage()); - } - - identifierToName.put(identifier, sb.toString()); + for (ASTExpression expression : node.findChildrenOfType(ASTExpression.class)) { + try { + dataNodeToName.putAll(expression.getDataNodes()); + } catch (ASTExpression.DataNodeStateException ignore) { + // Intentionally left blank + continue; } } - return identifierToName; + return dataNodeToName; } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java index 894967370c..94d5cdf281 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java @@ -47,8 +47,9 @@ public class VfParser extends AbstractParser { ASTCompilationUnit astCompilationUnit = new net.sourceforge.pmd.lang.vf.ast.VfParser( new SimpleCharStream(source)).CompilationUnit(); // Add type information to the AST - VfExpressionTypeVisitor visitor = new VfExpressionTypeVisitor(this.getParserOptions()); + VfExpressionTypeVisitor visitor = new VfExpressionTypeVisitor(fileName, this.getParserOptions().getProperties()); visitor.visit(astCompilationUnit, null); + return astCompilationUnit; } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java index d6a7a91e48..451d1d365a 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java @@ -4,13 +4,42 @@ package net.sourceforge.pmd.lang.vf; +import java.io.File; +import java.util.Collections; +import java.util.List; + import net.sourceforge.pmd.lang.ParserOptions; +import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertyFactory; public class VfParserOptions extends ParserOptions { + static final List DEFAULT_APEX_DIRECTORIES = Collections.singletonList(".." + File.separator + "classes"); + static final List DEFAULT_OBJECT_DIRECTORIES = Collections.singletonList(".." + File.separator + "objects"); + + /** + * Directory that contains Apex classes that may be referenced from a Visualforce page. + */ + public static final PropertyDescriptor> APEX_DIRECTORIES_DESCRIPTOR = + PropertyFactory.stringListProperty("apexDirectories") + .desc("Location of Apex Class directories. Absolute or relative to the Visualforce directory.") + .defaultValue(DEFAULT_APEX_DIRECTORIES) + .delim(',') + .build(); + + /** + * Directory that contains Object definitions that may be referenced from a Visualforce page. + */ + public static final PropertyDescriptor> OBJECTS_DIRECTORIES_DESCRIPTOR = + PropertyFactory.stringListProperty("objectsDirectories") + .desc("Location of CustomObject directories. Absolute or relative to the Visualforce directory.") + .defaultValue(DEFAULT_OBJECT_DIRECTORIES) + .delim(',') + .build(); + public VfParserOptions() { super(new VfLanguageModule()); - definePropertyDescriptor(VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR); - definePropertyDescriptor(VfExpressionTypeVisitor.OBJECTS_DIRECTORIES_DESCRIPTOR); + getProperties().definePropertyDescriptor(APEX_DIRECTORIES_DESCRIPTOR); + getProperties().definePropertyDescriptor(OBJECTS_DIRECTORIES_DESCRIPTOR); overridePropertiesFromEnv(); } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTExpression.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTExpression.java index a84eb74fb0..39977ce1f0 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTExpression.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTExpression.java @@ -4,9 +4,27 @@ package net.sourceforge.pmd.lang.vf.ast; +import java.util.IdentityHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import java.util.stream.Collectors; + import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.ast.AbstractTokenManager; +import net.sourceforge.pmd.lang.ast.Node; public class ASTExpression extends AbstractVFNode { + private static final Logger LOGGER = Logger.getLogger(ASTExpression.class.getName()); + + /** + * Thrown in cases where the the Identifiers in this node aren't ALL successfully parsed in a call to + * {@link #getDataNodes()} + */ + public static final class DataNodeStateException extends Exception { + } + @Deprecated @InternalApi public ASTExpression(int id) { @@ -23,4 +41,120 @@ public class ASTExpression extends AbstractVFNode { public Object jjtAccept(VfParserVisitor visitor, Object data) { return visitor.visit(this, data); } + + private void logWarning(String warning, Node node) { + LOGGER.warning(warning + + ". nodeClass=" + node.getClass().getSimpleName() + + ", fileName=" + AbstractTokenManager.getFileName() + + ", beginLine=" + node.getBeginLine() + + ", image=" + node.getImage()); + } + + /** + *

+ * An Expression can contain one or more strings that map to a piece of data. This method maps the string + * from the Visualforce page to terminal AST node that the string represents. The terminal node will be either an + * ASTIdentifier or ASTLiteral. It is the terminal node that is most important since it represents the type of data + * that will be displayed in the page. + *

+ *

+ * The string representation can be reconstructed by starting at the {@code Identifier} node and traversing its + * siblings until a node other than a {@code DotExpression} is encountered. Some more advanced situations aren't + * currently handled by this method. The method will throw an exception in such cases. + *

+ *
{@code
+     *  results in AST
+     * 
+     * The method would return key=ASTIdentifier(Image='MyValue'), value="MyValue"
+     * }
+ *
{@code
+     *  results in AST (It's important to notice that DotExpression is
+     * a sibling of Identifier.
+     * 
+     * 
+     *     
+     * 
+     * This method would return key=ASTIdentifier(Image='Text__c'), value="MyObject__c.Text__c"
+     * }
+ * + * THE FOLLOWING SITUATIONS ARE NOT HANDLED AND WILL THROW AN EXCEPTION. + * This syntax causes ambiguities with Apex Controller methods that return Maps versus accessing a CustomObject's + * field via array notation. This may be addressed in a future release. + * + *
{@code
+     *  results in AST
+     * 
+     * 
+     *     
+     * 
+
+     *  results in AST
+     * 
+     * 
+     *     
+     *         
+     *             
+     *         
+     *     
+     * 
+     * }
+ * + * @throws DataNodeStateException if the results of this method could have been incorrect. Callers should typically + * not rethrow this exception, as it will happen often and doesn't represent a terminal exception. + */ + public Map getDataNodes() throws DataNodeStateException { + Map result = new IdentityHashMap<>(); + + int numChildren = getNumChildren(); + List identifiers = findChildrenOfType(ASTIdentifier.class); + for (ASTIdentifier identifier : identifiers) { + LinkedList identifierNodes = new LinkedList<>(); + + // The Identifier is the first item that makes up the string + identifierNodes.add(identifier); + int index = identifier.getIndexInParent(); + + // Iterate through the rest of the children looking for ASTDotExpression nodes. + // The Image value of these nodes will be used to reconstruct the string. Any other node encountered will + // cause the while loop to break. The content of identifierNodes is used to construct the string and map + // it to the last element in identifierNodes. + index++; + while (index < numChildren) { + final Node node = getChild(index); + if (node instanceof ASTDotExpression) { + // The next part of the identifier will constructed from dot or array notation + if (node.getNumChildren() == 1) { + final Node expressionChild = node.getChild(0); + if (expressionChild instanceof ASTIdentifier || expressionChild instanceof ASTLiteral) { + identifierNodes.add((AbstractVFDataNode) expressionChild); + } else { + // This should never happen + logWarning("Node expected to be Identifier or Literal", node); + throw new DataNodeStateException(); + } + } else { + // This should never happen + logWarning("More than one child found for ASTDotExpression", node); + throw new DataNodeStateException(); + } + } else if (node instanceof ASTExpression) { + // Not currently supported. This can occur in a couple of cases that may be supported in the future. + // 1. Custom Field using array notation. MyObject__c['Text__c'] + // 2. An Apex method that returns a map. ControllerMethod['KeyForMap'] + throw new DataNodeStateException(); + } else { + // Any other node type is not considered part of the identifier and breaks out of the loop + break; + } + index++; + } + + // Convert the list of nodes to a string representation, store the last node in the list as the map's key + String idString = String.join(".", identifierNodes.stream() + .map(i -> i.getImage()) + .collect(Collectors.toList())); + result.put(identifierNodes.getLast(), idString); + } + return result; + } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTIdentifier.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTIdentifier.java index 21541527bb..717651b14f 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTIdentifier.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTIdentifier.java @@ -5,13 +5,8 @@ package net.sourceforge.pmd.lang.vf.ast; import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.vf.IdentifierType; -public class ASTIdentifier extends AbstractVFNode { - /** - * The data type that this identifier refers to. May be null. - */ - private IdentifierType identifierType; +public class ASTIdentifier extends AbstractVFDataNode { @Deprecated @InternalApi @@ -29,12 +24,4 @@ public class ASTIdentifier extends AbstractVFNode { public Object jjtAccept(VfParserVisitor visitor, Object data) { return visitor.visit(this, data); } - - public IdentifierType getIdentifierType() { - return identifierType; - } - - public void setIdentifierType(IdentifierType identifierType) { - this.identifierType = identifierType; - } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTLiteral.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTLiteral.java index 74450f65b8..f2516b54c6 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTLiteral.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTLiteral.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd.lang.vf.ast; import net.sourceforge.pmd.annotation.InternalApi; -public class ASTLiteral extends AbstractVFNode { +public class ASTLiteral extends AbstractVFDataNode { @Deprecated @InternalApi public ASTLiteral(int id) { diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java new file mode 100644 index 0000000000..280cadb7ef --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java @@ -0,0 +1,41 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.vf.DataType; + +/** + * Represents a node that displays a piece of data. + */ +@Deprecated +@InternalApi +public class AbstractVFDataNode extends AbstractVFNode { + private DataType dataType; + + public AbstractVFDataNode(int id) { + super(id); + } + + public AbstractVFDataNode(VfParser parser, int id) { + super(id); + this.parser = parser; + } + + /** + * Example XPath 1.0 and 2.0: {@code //Identifier[@DataType='DateTime']} + * + * @return data type that this node refers to. A null value indicates that no matching Metadata was found for this + * node. null differs from {@link DataType#Unknown} which indicates that Metadata was found but it wasn't mappable + * to one of the enums. + */ + public DataType getDataType() { + return dataType; + } + + public void setDataType(DataType dataType) { + this.dataType = dataType; + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java index 601a83323e..d41c086127 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java @@ -12,7 +12,7 @@ import java.util.Set; import java.util.regex.Pattern; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.vf.IdentifierType; +import net.sourceforge.pmd.lang.vf.DataType; import net.sourceforge.pmd.lang.vf.ast.ASTArguments; import net.sourceforge.pmd.lang.vf.ast.ASTAttribute; import net.sourceforge.pmd.lang.vf.ast.ASTContent; @@ -25,6 +25,7 @@ import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; import net.sourceforge.pmd.lang.vf.ast.ASTLiteral; import net.sourceforge.pmd.lang.vf.ast.ASTNegationExpression; import net.sourceforge.pmd.lang.vf.ast.ASTText; +import net.sourceforge.pmd.lang.vf.ast.AbstractVFDataNode; import net.sourceforge.pmd.lang.vf.ast.AbstractVFNode; import net.sourceforge.pmd.lang.vf.rule.AbstractVfRule; @@ -409,14 +410,12 @@ public class VfUnescapeElRule extends AbstractVfRule { continue; } + if (expressionContainsSafeDataNodes(expr)) { + continue; + } + final List ids = expr.findChildrenOfType(ASTIdentifier.class); - for (final ASTIdentifier id : ids) { - IdentifierType identifierType = id.getIdentifierType(); - if (identifierType != null && !identifierType.requiresEscaping) { - return false; - } - boolean isEscaped = false; for (Escaping e : escapes) { @@ -447,6 +446,24 @@ public class VfUnescapeElRule extends AbstractVfRule { return !nonEscapedIds.isEmpty(); } + /** + * Return true if the type of all data nodes can be determined and none of them require escaping + */ + private boolean expressionContainsSafeDataNodes(ASTExpression expression) { + try { + for (AbstractVFDataNode node : expression.getDataNodes().keySet()) { + DataType dataType = node.getDataType(); + if (dataType == null || dataType.requiresEscaping) { + return false; + } + } + + return true; + } catch (ASTExpression.DataNodeStateException e) { + return false; + } + } + private boolean containsSafeFields(final AbstractVFNode expression) { final ASTExpression ex = expression.getFirstChildOfType(ASTExpression.class); diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java index f490c7fe68..866f37c213 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesTest.java @@ -4,74 +4,57 @@ package net.sourceforge.pmd.lang.vf; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.junit.Test; public class ApexClassPropertyTypesTest { + private static final Map EXPECTED_DATA_TYPES; + + static { + // Intentionally use the wrong case for property names to ensure that they can be found. The Apex class name + // must have the correct case since it is used to lookup the file. The Apex class name is guaranteed to be correct + // in the Visualforce page, but the property names are not + EXPECTED_DATA_TYPES = new HashMap<>(); + EXPECTED_DATA_TYPES.put("ApexController.accOuntIdProp", DataType.Lookup); + EXPECTED_DATA_TYPES.put("ApexController.AcCountId", DataType.Lookup); + EXPECTED_DATA_TYPES.put("ApexController.AcCountname", DataType.Text); + + // InnerController + // The class should be parsed to Unknown. It's not a valid expression on its own. + EXPECTED_DATA_TYPES.put("ApexController.innErController", DataType.Unknown); + EXPECTED_DATA_TYPES.put("ApexController.innErController.innErAccountIdProp", DataType.Lookup); + EXPECTED_DATA_TYPES.put("ApexController.innErController.innErAccountid", DataType.Lookup); + EXPECTED_DATA_TYPES.put("ApexController.innErController.innErAccountnAme", DataType.Text); + + // Edge cases + // Invalid class should return null + EXPECTED_DATA_TYPES.put("unknownclass.invalidProperty", null); + // Invalid class property should return null + EXPECTED_DATA_TYPES.put("ApexController.invalidProperty", null); + /* + * It is possible to have a property and method with different types that resolve to the same Visualforce + * expression. An example is an Apex class with a property "public String Foo {get; set;}" and a method of + * "Integer getFoo() { return 1; }". These properties should map to {@link DataType#Unknown}. + */ + EXPECTED_DATA_TYPES.put("ApexController.ConflictingProp", DataType.Unknown); + } + @Test public void testApexClassIsProperlyParsed() { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) .resolve("SomePage.page"); - String vfFileName = vfPagePath.toString(); - - // Intentionally use the wrong case for property names to ensure that they can be found. The Apex class name - // must have the correct case since it is used to lookup the file. The Apex class name is guaranteed to be correct - // in the Visualforce page, but the property names are not ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); - assertEquals(IdentifierType.Lookup, - apexClassPropertyTypes.getVariableType("ApexController.accOuntIdProp", vfFileName, - VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); - assertEquals(IdentifierType.Lookup, - apexClassPropertyTypes.getVariableType("ApexController.AcCountId", vfFileName, - VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); - assertEquals(IdentifierType.Text, - apexClassPropertyTypes.getVariableType("ApexController.AcCountname", vfFileName, - VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); - // InnerController - assertEquals("The class should be parsed to Unknown. It's not a valid expression on its own.", - IdentifierType.Unknown, - apexClassPropertyTypes.getVariableType("ApexController.innErController", vfFileName, - VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); - assertEquals(IdentifierType.Lookup, - apexClassPropertyTypes.getVariableType("ApexController.innErController.innErAccountIdProp", - vfFileName, VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); - assertEquals(IdentifierType.Lookup, - apexClassPropertyTypes.getVariableType("ApexController.innErController.innErAccountid", - vfFileName, VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); - assertEquals(IdentifierType.Text, - apexClassPropertyTypes.getVariableType("ApexController.innErController.innErAccountnAme", - vfFileName, VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); - - assertNull("Invalid class should return null", - apexClassPropertyTypes.getVariableType("unknownclass.invalidProperty", vfFileName, - VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); - assertNull("Invalid class property should return null", - apexClassPropertyTypes.getVariableType("ApexController.invalidProperty", vfFileName, - VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); - } - - /** - * It is possible to have a property and method with different types that resolve to the same Visualforce - * expression. An example is an Apex class with a property "public String Foo {get; set;}" and a method of - * "Integer getFoo() { return 1; }". These properties should map to {@link IdentifierType#Unknown}. - */ - @Test - public void testConflictingPropertyTypesMapsToUnknown() { - Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) - .resolve("SomePage.page"); - String vfFileName = vfPagePath.toString(); - ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); - assertEquals(IdentifierType.Unknown, - apexClassPropertyTypes.getVariableType("ApexWithConflictingPropertyTypes.ConflictingProp", - vfFileName, VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR.defaultValue())); + VFTestUtils.validateDataTypes(EXPECTED_DATA_TYPES, apexClassPropertyTypes, vfPagePath, + VfParserOptions.APEX_DIRECTORIES_DESCRIPTOR.defaultValue()); } @Test @@ -82,6 +65,6 @@ public class ApexClassPropertyTypesTest { List paths = Arrays.asList(Paths.get("..", "classes-does-not-exist").toString()); ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); - assertNull(apexClassPropertyTypes.getVariableType("ApexController.accOuntIdProp", vfFileName, paths)); + assertNull(apexClassPropertyTypes.getDataType("ApexController.accOuntIdProp", vfFileName, paths)); } } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/DataTypeTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/DataTypeTest.java new file mode 100644 index 0000000000..0accbd9625 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/DataTypeTest.java @@ -0,0 +1,38 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import apex.jorje.semantic.symbol.type.BasicType; + +public class DataTypeTest { + @Test + public void testFromString() { + assertEquals(DataType.AutoNumber, DataType.fromString("AutoNumber")); + assertEquals(DataType.AutoNumber, DataType.fromString("autonumber")); + assertEquals(DataType.Unknown, DataType.fromString("")); + assertEquals(DataType.Unknown, DataType.fromString(null)); + } + + @Test + public void testFromBasicType() { + assertEquals(DataType.Checkbox, DataType.fromBasicType(BasicType.BOOLEAN)); + assertEquals(DataType.Number, DataType.fromBasicType(BasicType.DECIMAL)); + assertEquals(DataType.Number, DataType.fromBasicType(BasicType.DOUBLE)); + assertEquals(DataType.Unknown, DataType.fromBasicType(BasicType.APEX_OBJECT)); + assertEquals(DataType.Unknown, DataType.fromBasicType(null)); + } + + @Test + public void testRequiresEncoding() { + assertFalse(DataType.AutoNumber.requiresEscaping); + assertTrue(DataType.Text.requiresEscaping); + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/IdentifierTypeTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/IdentifierTypeTest.java deleted file mode 100644 index 88d07113dc..0000000000 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/IdentifierTypeTest.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.vf; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -import apex.jorje.semantic.symbol.type.BasicType; - -public class IdentifierTypeTest { - @Test - public void testFromString() { - assertEquals(IdentifierType.AutoNumber, IdentifierType.fromString("AutoNumber")); - assertEquals(IdentifierType.AutoNumber, IdentifierType.fromString("autonumber")); - assertEquals(IdentifierType.Unknown, IdentifierType.fromString("")); - assertEquals(IdentifierType.Unknown, IdentifierType.fromString(null)); - } - - @Test - public void testFromBasicType() { - assertEquals(IdentifierType.Checkbox, IdentifierType.fromBasicType(BasicType.BOOLEAN)); - assertEquals(IdentifierType.Number, IdentifierType.fromBasicType(BasicType.DECIMAL)); - assertEquals(IdentifierType.Number, IdentifierType.fromBasicType(BasicType.DOUBLE)); - assertEquals(IdentifierType.Unknown, IdentifierType.fromBasicType(BasicType.APEX_OBJECT)); - assertEquals(IdentifierType.Unknown, IdentifierType.fromBasicType(null)); - } - - @Test - public void testRequiresEncoding() { - assertFalse(IdentifierType.AutoNumber.requiresEscaping); - assertTrue(IdentifierType.Text.requiresEscaping); - } -} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java index ba9dea1421..398d892eb6 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ObjectFieldTypesTest.java @@ -4,18 +4,45 @@ package net.sourceforge.pmd.lang.vf; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.junit.Test; public class ObjectFieldTypesTest { + private static final Map EXPECTED_SFDX_DATA_TYPES; + private static final Map EXPECTED_MDAPI_DATA_TYPES; + + static { + EXPECTED_SFDX_DATA_TYPES = new HashMap<>(); + EXPECTED_SFDX_DATA_TYPES.put("Account.Checkbox__c", DataType.Checkbox); + EXPECTED_SFDX_DATA_TYPES.put("Account.DateTime__c", DataType.DateTime); + EXPECTED_SFDX_DATA_TYPES.put("Account.LongTextArea__c", DataType.LongTextArea); + EXPECTED_SFDX_DATA_TYPES.put("Account.Picklist__c", DataType.Picklist); + EXPECTED_SFDX_DATA_TYPES.put("Account.Text__c", DataType.Text); + EXPECTED_SFDX_DATA_TYPES.put("Account.TextArea__c", DataType.TextArea); + // Edge Cases + // Invalid property should return null + EXPECTED_SFDX_DATA_TYPES.put("Account.DoesNotExist__c", null); + + EXPECTED_MDAPI_DATA_TYPES = new HashMap<>(); + EXPECTED_MDAPI_DATA_TYPES.put("Account.MDCheckbox__c", DataType.Checkbox); + EXPECTED_MDAPI_DATA_TYPES.put("Account.MDDateTime__c", DataType.DateTime); + EXPECTED_MDAPI_DATA_TYPES.put("Account.MDLongTextArea__c", DataType.LongTextArea); + EXPECTED_MDAPI_DATA_TYPES.put("Account.MDPicklist__c", DataType.Picklist); + EXPECTED_MDAPI_DATA_TYPES.put("Account.MDText__c", DataType.Text); + EXPECTED_MDAPI_DATA_TYPES.put("Account.MDTextArea__c", DataType.TextArea); + // Edge Cases + // Invalid property should return null + EXPECTED_MDAPI_DATA_TYPES.put("Account.DoesNotExist__c", null); + } /** * Verify that CustomFields stored in sfdx project format are correctly parsed @@ -25,7 +52,7 @@ public class ObjectFieldTypesTest { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf).resolve("SomePage.page"); ObjectFieldTypes objectFieldTypes = new ObjectFieldTypes(); - validateSfdxAccount(objectFieldTypes, vfPagePath, VfExpressionTypeVisitor.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue()); + validateSfdxAccount(objectFieldTypes, vfPagePath, VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue()); } /** @@ -36,7 +63,7 @@ public class ObjectFieldTypesTest { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.MDAPI, VFTestUtils.MetadataType.Vf).resolve("SomePage.page"); ObjectFieldTypes objectFieldTypes = new ObjectFieldTypes(); - validateMDAPIAccount(objectFieldTypes, vfPagePath, VfExpressionTypeVisitor.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue()); + validateMDAPIAccount(objectFieldTypes, vfPagePath, VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue()); } /** @@ -48,7 +75,7 @@ public class ObjectFieldTypesTest { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) .resolve("SomePage.page"); - List paths = Arrays.asList(VfExpressionTypeVisitor.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue().get(0), + List paths = Arrays.asList(VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue().get(0), VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.MDAPI, VFTestUtils.MetadataType.Objects).toString()); objectFieldTypes = new ObjectFieldTypes(); validateSfdxAccount(objectFieldTypes, vfPagePath, paths); @@ -67,48 +94,20 @@ public class ObjectFieldTypesTest { List paths = Arrays.asList(Paths.get("..", "objects-does-not-exist").toString()); ObjectFieldTypes objectFieldTypes = new ObjectFieldTypes(); - assertNull(objectFieldTypes.getVariableType("Account.DoesNotExist__c", vfFileName, paths)); + assertNull(objectFieldTypes.getDataType("Account.DoesNotExist__c", vfFileName, paths)); } /** * Validate the expected results when the Account Fields are stored in decomposed sfdx format */ private void validateSfdxAccount(ObjectFieldTypes objectFieldTypes, Path vfPagePath, List paths) { - String vfFileName = vfPagePath.toString(); - - assertEquals(IdentifierType.Checkbox, - objectFieldTypes.getVariableType("Account.Checkbox__c", vfFileName, paths)); - assertEquals(IdentifierType.DateTime, - objectFieldTypes.getVariableType("Account.DateTime__c", vfFileName, paths)); - assertEquals(IdentifierType.LongTextArea, - objectFieldTypes.getVariableType("Account.LongTextArea__c", vfFileName, paths)); - assertEquals(IdentifierType.Picklist, - objectFieldTypes.getVariableType("Account.Picklist__c", vfFileName, paths)); - assertEquals(IdentifierType.Text, - objectFieldTypes.getVariableType("Account.Text__c", vfFileName, paths)); - assertEquals(IdentifierType.TextArea, - objectFieldTypes.getVariableType("Account.TextArea__c", vfFileName, paths)); - assertNull(objectFieldTypes.getVariableType("Account.DoesNotExist__c", vfFileName, paths)); + VFTestUtils.validateDataTypes(EXPECTED_SFDX_DATA_TYPES, objectFieldTypes, vfPagePath, paths); } /** * Validate the expected results when the Account Fields are stored in a single file MDAPI format */ private void validateMDAPIAccount(ObjectFieldTypes objectFieldTypes, Path vfPagePath, List paths) { - String vfFileName = vfPagePath.toString(); - - assertEquals(IdentifierType.Checkbox, - objectFieldTypes.getVariableType("Account.MDCheckbox__c", vfFileName, paths)); - assertEquals(IdentifierType.DateTime, - objectFieldTypes.getVariableType("Account.MDDateTime__c", vfFileName, paths)); - assertEquals(IdentifierType.LongTextArea, - objectFieldTypes.getVariableType("Account.MDLongTextArea__c", vfFileName, paths)); - assertEquals(IdentifierType.Picklist, - objectFieldTypes.getVariableType("Account.MDPicklist__c", vfFileName, paths)); - assertEquals(IdentifierType.Text, - objectFieldTypes.getVariableType("Account.MDText__c", vfFileName, paths)); - assertEquals(IdentifierType.TextArea, - objectFieldTypes.getVariableType("Account.MDTextArea__c", vfFileName, paths)); - assertNull(objectFieldTypes.getVariableType("Account.DoesNotExist__c", vfFileName, paths)); + VFTestUtils.validateDataTypes(EXPECTED_MDAPI_DATA_TYPES, objectFieldTypes, vfPagePath, paths); } } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestUtils.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestUtils.java index 3259b83425..e403ecd178 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestUtils.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VFTestUtils.java @@ -4,8 +4,19 @@ package net.sourceforge.pmd.lang.vf; +import static org.junit.Assert.assertEquals; + import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.rule.xpath.SaxonXPathRuleQuery; +import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery; +import net.sourceforge.pmd.properties.PropertyDescriptor; public final class VFTestUtils { /** @@ -61,6 +72,36 @@ public final class VFTestUtils { return path.toAbsolutePath(); } + /** + * @return all nodes that match the {@code xpath} version 2 query. + */ + public static List findNodes(Node node, String xpath) { + SaxonXPathRuleQuery query = createQuery(xpath); + return query.evaluate(node, new RuleContext()); + } + + /** + * Verify that return values of {@link SalesforceFieldTypes#getDataType(String, String, List)} using the keys of + * {@code expectedDataTypes} matches the values of {@code expectedDataTypes} + */ + public static void validateDataTypes(Map expectedDataTypes, SalesforceFieldTypes fieldTypes, + Path vfPagePath, List paths) { + String vfFileName = vfPagePath.toString(); + + for (Map.Entry entry : expectedDataTypes.entrySet()) { + assertEquals(entry.getKey(), entry.getValue(), + fieldTypes.getDataType(entry.getKey(), vfFileName, paths)); + } + } + + private static SaxonXPathRuleQuery createQuery(String xpath) { + SaxonXPathRuleQuery query = new SaxonXPathRuleQuery(); + query.setVersion(XPathRuleQuery.XPATH_2_0); + query.setProperties(Collections., Object>emptyMap()); + query.setXPath(xpath); + return query; + } + private VFTestUtils() { } } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitorTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitorTest.java new file mode 100644 index 0000000000..0997a9695e --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitorTest.java @@ -0,0 +1,187 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.Parser; +import net.sourceforge.pmd.lang.ParserOptions; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; +import net.sourceforge.pmd.lang.vf.ast.ASTLiteral; +import net.sourceforge.pmd.lang.vf.ast.AbstractVFDataNode; +import net.sourceforge.pmd.util.treeexport.XmlTreeRenderer; + +public class VfExpressionTypeVisitorTest { + private static final Map EXPECTED_CUSTOM_FIELD_DATA_TYPES; + private static final Map EXPECTED_APEX_DATA_TYPES; + + static { + EXPECTED_CUSTOM_FIELD_DATA_TYPES = new HashMap<>(); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("CreatedDate", DataType.DateTime); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("DateTime__c", DataType.DateTime); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("Checkbox__c", DataType.Checkbox); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("Name", DataType.Text); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("Text__c", DataType.Text); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("TextArea__c", DataType.TextArea); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("LongTextArea__c", DataType.LongTextArea); + EXPECTED_CUSTOM_FIELD_DATA_TYPES.put("Picklist__c", DataType.Picklist); + + EXPECTED_APEX_DATA_TYPES = new HashMap<>(); + EXPECTED_APEX_DATA_TYPES.put("AccountIdProp", DataType.Lookup); + EXPECTED_APEX_DATA_TYPES.put("AccountId", DataType.Lookup); + EXPECTED_APEX_DATA_TYPES.put("InnerAccountId", DataType.Lookup); + EXPECTED_APEX_DATA_TYPES.put("InnerAccountIdProp", DataType.Lookup); + EXPECTED_APEX_DATA_TYPES.put("AccountName", DataType.Text); + EXPECTED_APEX_DATA_TYPES.put("InnerAccountName", DataType.Text); + EXPECTED_APEX_DATA_TYPES.put("ConflictingProp", DataType.Unknown); + } + + /** + * Strings that use dot notation(Account.CreatedDate) result in ASTIdentifier nodes + */ + @Test + public void testXpathQueryForCustomFieldIdentifiers() throws FileNotFoundException { + Node rootNode = compile("StandardAccount.page"); + + for (Map.Entry entry : EXPECTED_CUSTOM_FIELD_DATA_TYPES.entrySet()) { + String xpath = String.format("//Identifier[@Image='%s' and @DataType='%s']", entry.getKey(), entry.getValue().name()); + List nodes = VFTestUtils.findNodes(rootNode, xpath); + // Each string appears twice, it is set on a "value" attribute and inline + assertEquals(entry.getKey(), 2, nodes.size()); + for (Node node : nodes) { + assertEquals(entry.getKey(), node.getImage()); + assertTrue(node.getClass().getSimpleName(), node instanceof ASTIdentifier); + ASTIdentifier identifier = (ASTIdentifier) node; + assertEquals(entry.getKey(), entry.getValue(), identifier.getDataType()); + } + } + } + + /** + * Strings that use array notation, Account['CreatedDate') don't have a DataType added. This type of notation + * creates ambiguous situations with Apex methods that return Maps. This may be addressed in a future release. + */ + @Test + public void testXpathQueryForCustomFieldLiteralsHaveNullDataType() throws FileNotFoundException { + Node rootNode = compile("StandardAccount.page"); + + for (Map.Entry entry : EXPECTED_CUSTOM_FIELD_DATA_TYPES.entrySet()) { + // Literals are surrounded by apostrophes + String xpath = String.format("//Literal[@Image=\"'%s'\" and @DataType='']", entry.getKey()); + List nodes = VFTestUtils.findNodes(rootNode, xpath); + // Each string appears twice, it is set on a "value" attribute and inline + assertEquals(entry.getKey(), 2, nodes.size()); + for (Node node : nodes) { + assertEquals(String.format("'%s'", entry.getKey()), node.getImage()); + assertTrue(node.getClass().getSimpleName(), node instanceof ASTLiteral); + ASTLiteral literal = (ASTLiteral) node; + assertEquals(entry.getKey(), null, literal.getDataType()); + } + } + } + + /** + * Nodes where the DataType can't be determined should have a null DataType + */ + @Test + public void testDataTypeForCustomFieldsNotFound() throws FileNotFoundException { + Node rootNode = compile("StandardAccount.page"); + + for (String xpath : new String[] { "//Identifier[@Image='NotFoundField__c']", "//Literal[@Image=\"'NotFoundField__c'\"]" }) { + List nodes = VFTestUtils.findNodes(rootNode, xpath); + // Each string appears twice, it is set on a "value" attribute and inline + assertEquals(2, nodes.size()); + for (Node node : nodes) { + assertTrue(node.getClass().getSimpleName(), node instanceof AbstractVFDataNode); + AbstractVFDataNode dataNode = (AbstractVFDataNode) node; + assertNull(dataNode.getDataType()); + } + } + } + + /** + * Apex properties result in ASTIdentifier nodes + */ + @Test + public void testXpathQueryForProperties() throws FileNotFoundException { + Node rootNode = compile("ApexController.page"); + + for (Map.Entry entry : EXPECTED_APEX_DATA_TYPES.entrySet()) { + String xpath = String.format("//Identifier[@Image='%s' and @DataType='%s']", entry.getKey(), entry.getValue().name()); + List nodes = VFTestUtils.findNodes(rootNode, xpath); + // Each string appears twice, it is set on a "value" attribute and inline + assertEquals(entry.getKey(), 2, nodes.size()); + for (Node node : nodes) { + assertEquals(entry.getKey(), node.getImage()); + assertTrue(node.getClass().getSimpleName(), node instanceof ASTIdentifier); + ASTIdentifier identifier = (ASTIdentifier) node; + assertEquals(entry.getKey(), entry.getValue(), identifier.getDataType()); + } + } + } + + /** + * Nodes where the DataType can't be determined should have a null DataType + */ + @Test + public void testDataTypeForApexPropertiesNotFound() throws FileNotFoundException { + Node rootNode = compile("ApexController.page"); + + String xpath = "//Identifier[@Image='NotFoundProp']"; + List nodes = VFTestUtils.findNodes(rootNode, xpath); + // Each string appears twice, it is set on a "value" attribute and inline + assertEquals(2, nodes.size()); + for (Node node : nodes) { + assertTrue(node.getClass().getSimpleName(), node instanceof AbstractVFDataNode); + AbstractVFDataNode dataNode = (AbstractVFDataNode) node; + assertNull(dataNode.getDataType()); + } + } + + private Node compile(String pageName) throws FileNotFoundException { + return compile(pageName, false); + } + + private Node compile(String pageName, boolean renderAST) throws FileNotFoundException { + Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) + .resolve(pageName); + return compile(vfPagePath, renderAST); + } + + private Node compile(Path vfPagePath, boolean renderAST) throws FileNotFoundException { + LanguageVersion languageVersion = LanguageRegistry.getLanguage(VfLanguageModule.NAME).getDefaultVersion(); + ParserOptions parserOptions = languageVersion.getLanguageVersionHandler().getDefaultParserOptions(); + Parser parser = languageVersion.getLanguageVersionHandler().getParser(parserOptions); + + Node node = parser.parse(vfPagePath.toString(), new FileReader(vfPagePath.toFile())); + assertNotNull(node); + + if (renderAST) { + try { + new XmlTreeRenderer().renderSubtree(node, System.out); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return node; + } +} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java index c20faef000..0a34e45fe4 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java @@ -12,9 +12,9 @@ public class VfParserOptionsTest { @Test public void testDefaultPropertyDescriptors() { VfParserOptions vfParserOptions = new VfParserOptions(); - assertEquals(VfExpressionTypeVisitor.DEFAULT_APEX_DIRECTORIES, - vfParserOptions.getProperty(VfExpressionTypeVisitor.APEX_DIRECTORIES_DESCRIPTOR)); - assertEquals(VfExpressionTypeVisitor.DEFAULT_OBJECT_DIRECTORIES, - vfParserOptions.getProperty(VfExpressionTypeVisitor.OBJECTS_DIRECTORIES_DESCRIPTOR)); + assertEquals(VfParserOptions.DEFAULT_APEX_DIRECTORIES, + vfParserOptions.getProperties().getProperty(VfParserOptions.APEX_DIRECTORIES_DESCRIPTOR)); + assertEquals(VfParserOptions.DEFAULT_OBJECT_DIRECTORIES, + vfParserOptions.getProperties().getProperty(VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR)); } } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ASTExpressionTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ASTExpressionTest.java new file mode 100644 index 0000000000..d432b1b8e3 --- /dev/null +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ASTExpressionTest.java @@ -0,0 +1,237 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.StringReader; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.junit.Test; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.SimpleCharStream; +import net.sourceforge.pmd.lang.vf.VFTestUtils; +import net.sourceforge.pmd.util.treeexport.XmlTreeRenderer; + +public class ASTExpressionTest { + /** + * Slightly different scenarios which cause different AST, but should return the same results. + */ + private static final String[] SNIPPET_TEMPLATES = new String[] { + "{!%s}", + "", + "" }; + + @Test + public void testExpressionWithApexGetter() throws ASTExpression.DataNodeStateException { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyValue")); + + List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + assertEquals(template, 1, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + Map identifiers = expression.getDataNodes(); + assertEquals(template, 1, identifiers.size()); + + Map map = invertMap(identifiers); + assertTrue(template, map.containsKey("MyValue")); + assertTrue(template, map.get("MyValue") instanceof ASTIdentifier); + } + } + + @Test + public void testExpressionWithStandardController() throws ASTExpression.DataNodeStateException { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject__c.Text__c")); + + List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + assertEquals(template, 1, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + Map identifiers = expression.getDataNodes(); + assertEquals(template, 1, identifiers.size()); + + Map map = invertMap(identifiers); + assertTrue(template, map.containsKey("MyObject__c.Text__c")); + assertTrue(template, map.get("MyObject__c.Text__c") instanceof ASTIdentifier); + } + } + + @Test + public void testSelectOptions() throws ASTExpression.DataNodeStateException { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "userOptions.0")); + + List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + assertEquals(template, 1, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + Map identifiers = expression.getDataNodes(); + assertEquals(template, 1, identifiers.size()); + + Map map = invertMap(identifiers); + assertTrue(template, map.containsKey("userOptions.0")); + assertTrue(template, map.get("userOptions.0") instanceof ASTLiteral); + } + } + + @Test + public void testMultipleIdentifiers() throws ASTExpression.DataNodeStateException { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject__c.Text__c + ' this is a string' + MyObject__c.Text2__c")); + + List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + assertEquals(template, 1, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + Map identifiers = expression.getDataNodes(); + assertEquals(template, 2, identifiers.size()); + + Map map = invertMap(identifiers); + assertEquals(template, 2, map.size()); + assertTrue(template, map.containsKey("MyObject__c.Text__c")); + assertTrue(template, map.get("MyObject__c.Text__c") instanceof ASTIdentifier); + assertTrue(template, map.containsKey("MyObject__c.Text2__c")); + assertTrue(template, map.get("MyObject__c.Text2__c") instanceof ASTIdentifier); + } + } + + @Test + public void testIdentifierWithRelation() throws ASTExpression.DataNodeStateException { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject1__c.MyObject2__r.Text__c")); + + List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + assertEquals(template, 1, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + Map identifiers = expression.getDataNodes(); + assertEquals(template, 1, identifiers.size()); + + Map map = invertMap(identifiers); + assertEquals(template, 1, map.size()); + assertTrue(template, map.containsKey("MyObject1__c.MyObject2__r.Text__c")); + assertTrue(template, map.get("MyObject1__c.MyObject2__r.Text__c") instanceof ASTIdentifier); + } + } + + @Test + public void testMultipleIdentifiersWithRelation() throws ASTExpression.DataNodeStateException { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject1__c.MyObject2__r.Text__c + ' this is a string' + MyObject1__c.MyObject2__r.Text2__c")); + + List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + assertEquals(template, 1, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + Map identifiers = expression.getDataNodes(); + assertEquals(template, 2, identifiers.size()); + + Map map = invertMap(identifiers); + assertEquals(template, 2, map.size()); + assertTrue(template, map.containsKey("MyObject1__c.MyObject2__r.Text__c")); + assertTrue(template, map.get("MyObject1__c.MyObject2__r.Text__c") instanceof ASTIdentifier); + assertTrue(template, map.containsKey("MyObject1__c.MyObject2__r.Text2__c")); + assertTrue(template, map.get("MyObject1__c.MyObject2__r.Text2__c") instanceof ASTIdentifier); + } + } + + /** + * The current implementation does not support expressing statements using array notation. This notation introduces + * complexities that may be addressed in a future release. + */ + @Test + public void testExpressionWithArrayIndexingNotSupported() { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject__c['Name']")); + + List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + assertEquals(template, 2, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + try { + expression.getDataNodes(); + fail(template + " should have thrown"); + } catch (ASTExpression.DataNodeStateException expected) { + // Intentionally left blank + } + } + } + + @Test + public void testIdentifierWithRelationIndexedAsArrayNotSupported() { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject1__c['MyObject2__r'].Text__c")); + + List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + assertEquals(template, 2, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + try { + expression.getDataNodes(); + fail(template + " should have thrown"); + } catch (ASTExpression.DataNodeStateException expected) { + // Intentionally left blank + } + } + } + + @Test + public void testIdentifierWithComplexIndexedArrayNotSupported() { + for (String template : SNIPPET_TEMPLATES) { + ASTCompilationUnit compilationUnit = compile(String.format(template, "theLineItems[item.Id].UnitPrice")); + + List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + assertEquals(template, 2, nodes.size()); + + ASTExpression expression = (ASTExpression) nodes.get(0); + try { + expression.getDataNodes(); + fail(template + " should have thrown"); + } catch (ASTExpression.DataNodeStateException expected) { + // Intentionally left blank + } + } + } + + /** + * Invert the map to make it easier to unit test. + */ + private Map invertMap(Map map) { + Map result = map.entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); + // Ensure no values have been lost + assertEquals(map.size(), result.size()); + return result; + } + + private ASTCompilationUnit compile(String snippet) { + return compile(snippet, false); + } + + private ASTCompilationUnit compile(String snippet, boolean renderAST) { + ASTCompilationUnit node = new net.sourceforge.pmd.lang.vf.ast.VfParser( + new SimpleCharStream(new StringReader("" + + snippet + + ""))).CompilationUnit(); + + if (renderAST) { + try { + new XmlTreeRenderer().renderSubtree(node, System.out); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + return node; + } +} 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 df2567286a..5a407676a8 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 @@ -47,12 +47,18 @@ public class VfUnescapeElTest extends PmdRuleTst { Report report = runRule(vfPagePath); List ruleViolations = report.getViolations(); - assertEquals(6, ruleViolations.size()); - int firstLineWithErrors = 7; + assertEquals("Number of violations", 20, ruleViolations.size()); + + int firstLineWithErrors = 14; for (int i = 0; i < ruleViolations.size(); i++) { RuleViolation ruleViolation = ruleViolations.get(i); assertEquals(EXPECTED_RULE_MESSAGE, ruleViolation.getDescription()); - assertEquals(firstLineWithErrors + i, ruleViolation.getBeginLine()); + int expectedLineNumber = firstLineWithErrors + i; + if ((ruleViolations.size() + firstLineWithErrors - 1) == expectedLineNumber) { + // The last line has two errors on the same page + expectedLineNumber = expectedLineNumber - 1; + } + assertEquals("Line Number", expectedLineNumber, ruleViolation.getBeginLine()); } } @@ -65,12 +71,12 @@ public class VfUnescapeElTest extends PmdRuleTst { Report report = runRule(vfPagePath); List ruleViolations = report.getViolations(); - assertEquals(6, ruleViolations.size()); + assertEquals("Number of violations", 6, ruleViolations.size()); int firstLineWithErrors = 8; for (int i = 0; i < ruleViolations.size(); i++) { RuleViolation ruleViolation = ruleViolations.get(i); assertEquals(EXPECTED_RULE_MESSAGE, ruleViolation.getDescription()); - assertEquals(firstLineWithErrors + i, ruleViolation.getBeginLine()); + assertEquals("Line Number", firstLineWithErrors + i, ruleViolation.getBeginLine()); } } @@ -83,13 +89,13 @@ public class VfUnescapeElTest extends PmdRuleTst { Report report = runRule(vfPagePath); List ruleViolations = report.getViolations(); - assertEquals(2, ruleViolations.size()); + assertEquals("Number of violations", 2, ruleViolations.size()); int firstLineWithErrors = 9; for (int i = 0; i < ruleViolations.size(); i++) { // There should start at line 9 RuleViolation ruleViolation = ruleViolations.get(i); assertEquals(EXPECTED_RULE_MESSAGE, ruleViolation.getDescription()); - assertEquals(firstLineWithErrors + i, ruleViolation.getBeginLine()); + assertEquals("Line Number", firstLineWithErrors + i, ruleViolation.getBeginLine()); } } diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.cls index df449e3e6f..76fdac2c62 100644 --- a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.cls +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.cls @@ -1,5 +1,6 @@ public class ApexController { public Id AccountIdProp { get; set; } + public String ConflictingProp { get; set; } public ApexController() { acc = [SELECT Id, Name, Site FROM Account @@ -15,6 +16,10 @@ public class ApexController { return acc.name; } + public Integer getConflictingProp() { + return ''; + } + public InnerController getInnerController() { return new InnerController(this); } diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexWithConflictingPropertyTypes.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexWithConflictingPropertyTypes.cls deleted file mode 100644 index f166301e47..0000000000 --- a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexWithConflictingPropertyTypes.cls +++ /dev/null @@ -1,7 +0,0 @@ -public class ApexWithConflictingPropertyTypes { - public String ConflictingProp { get; set; } - - public Integer getConflictingProp() { - return ''; - } -} \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page index d1e8d3fb88..c0090110b0 100644 --- a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page @@ -1 +1,3 @@ - + +The contents of this page aren't relevant to the test, but the test requires the file to be present. + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page index d1e8d3fb88..c0090110b0 100644 --- a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page @@ -1 +1,3 @@ - + +The contents of this page aren't relevant to the test, but the test requires the file to be present. + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page index d1e8d3fb88..c0090110b0 100644 --- a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page @@ -1 +1,3 @@ - + +The contents of this page aren't relevant to the test, but the test requires the file to be present. + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/classes/ApexController.cls b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/classes/ApexController.cls new file mode 100644 index 0000000000..76fdac2c62 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/classes/ApexController.cls @@ -0,0 +1,44 @@ +public class ApexController { + public Id AccountIdProp { get; set; } + public String ConflictingProp { get; set; } + + public ApexController() { + acc = [SELECT Id, Name, Site FROM Account + WHERE Id = :ApexPages.currentPage().getParameters().get('id')]; + this.AccountIdProp = acc.Id; + } + + public Id getAccountId() { + return acc.id; + } + + public String getAccountName() { + return acc.name; + } + + public Integer getConflictingProp() { + return ''; + } + + public InnerController getInnerController() { + return new InnerController(this); + } + + public class InnerController { + private ApexController parent; + public Id InnerAccountIdProp { get; set; } + + public InnerController(ApexController parent) { + this.parent = parent; + this.InnerAccountIdProp = parent.AccountIdProp; + } + + public Id getInnerAccountId() { + return 'Inner: ' + parent.acc.id; + } + + public String getInnerAccountName() { + return 'Inner: ' + parent.acc.name; + } + } +} \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml new file mode 100644 index 0000000000..03ffc46229 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml @@ -0,0 +1,9 @@ + + + Checkbox__c + false + false + + false + Checkbox + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml new file mode 100644 index 0000000000..5e0ce1cd2c --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml @@ -0,0 +1,9 @@ + + + DateTime__c + false + + false + false + DateTime + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml new file mode 100644 index 0000000000..6de4650b1f --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml @@ -0,0 +1,10 @@ + + + LongTextArea__c + false + + 32768 + false + LongTextArea + 3 + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml new file mode 100644 index 0000000000..597d9d3d30 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml @@ -0,0 +1,24 @@ + + + Picklist__c + false + + false + false + Picklist + + + false + + Value1 + false + + + + Value2 + false + + + + + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml new file mode 100644 index 0000000000..cd59083b9e --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml @@ -0,0 +1,9 @@ + + + TextArea__c + false + + false + false + TextArea + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml new file mode 100644 index 0000000000..efeeb5262a --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml @@ -0,0 +1,11 @@ + + + Text__c + false + + 255 + false + false + Text + false + diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/pages/ApexController.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/pages/ApexController.page new file mode 100644 index 0000000000..b876d5f46b --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/pages/ApexController.page @@ -0,0 +1,18 @@ + + + {!AccountIdProp} + + {!AccountId} + + {!InnerController.InnerAccountId} + + {!InnerController.InnerAccountIdProp} + + {!AccountName} + + {!InnerController.InnerAccountName} + + {!ConflictingProp} + + {!NotFoundProp} + \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/pages/StandardAccount.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/pages/StandardAccount.page new file mode 100644 index 0000000000..67851687c1 --- /dev/null +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/pages/StandardAccount.page @@ -0,0 +1,41 @@ + + + + {!Account.CreatedDate} + + {!Account.Checkbox__c} + + {!Account.DateTime__c} + + {!Account.Name} + + {!Account.Text__c} + + {!Account.TextArea__c} + + {!Account.LongTextArea__c} + + {!Account.Picklist__c} + + {!Account.NotFoundField__c} + + + + {!Account['CreatedDate']} + + {!Account['Checkbox__c']} + + {!Account['DateTime__c']} + + {!Account['Name']} + + {!Account['Text__c']} + + {!Account['TextArea__c']} + + {!Account['LongTextArea__c']} + + {!Account['Picklist__c']} + + {!Account['NotFoundField__c']} + \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/pages/StandardAccount.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/pages/StandardAccount.page index 628375b2d2..8413160a63 100644 --- a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/pages/StandardAccount.page +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/mdapi/pages/StandardAccount.page @@ -10,5 +10,5 @@ - + \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccount.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccount.page index d470c4f5f6..c95635785f 100644 --- a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccount.page +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccount.page @@ -1,13 +1,33 @@ + {!Account.CreatedDate} + {!Account.Checkbox__c} + + + {!Account.DateTime__c} + + + + + + + + + + - + + + + + + \ No newline at end of file diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccountWithExtensions.page b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccountWithExtensions.page index aef9bd6abf..0fcc494275 100644 --- a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccountWithExtensions.page +++ b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeEl/metadata/sfdx/pages/StandardAccountWithExtensions.page @@ -11,7 +11,7 @@ - + \ No newline at end of file From 21506cea859cc1dec6ce2d71c339be219568be87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 24 Nov 2020 11:39:59 +0100 Subject: [PATCH 32/98] Cleanup languages to skip --- .../pmd/AbstractRuleSetFactoryTest.java | 14 ++++++-------- .../pmd/lang/vf/RuleSetFactoryTest.java | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java index 0c21329fea..814a349387 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java @@ -60,22 +60,20 @@ public abstract class AbstractRuleSetFactoryTest { private static SAXParser saxParser; protected Set validXPathClassNames = new HashSet<>(); - private Set languagesToSkip = new HashSet<>(); + private final Set languagesToSkip = new HashSet<>(); public AbstractRuleSetFactoryTest() { - this(null); + this(new String[0]); } /** * Constructor used when a module that depends on another module wants to filter out the dependee's rulesets. * - * @param languagesToSkip {@link Language}s that appear in the classpath via a dependency, but should be + * @param languagesToSkip {@link Language}s terse names that appear in the classpath via a dependency, but should be * skipped because they aren't the primary language which the concrete instance of this class is testing. */ - public AbstractRuleSetFactoryTest(Language... languagesToSkip) { - if (languagesToSkip != null) { - this.languagesToSkip.addAll(Arrays.asList(languagesToSkip)); - } + public AbstractRuleSetFactoryTest(String... languagesToSkip) { + this.languagesToSkip.addAll(Arrays.asList(languagesToSkip)); validXPathClassNames.add(XPathRule.class.getName()); } @@ -278,7 +276,7 @@ public abstract class AbstractRuleSetFactoryTest { List result = new ArrayList<>(); for (Language language : LanguageRegistry.getLanguages()) { - if (this.languagesToSkip.contains(language)) { + if (this.languagesToSkip.contains(language.getTerseName())) { continue; } result.addAll(getRuleSetFileNames(language.getTerseName())); diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java index 1e0e05f181..3ffadc6a4a 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/RuleSetFactoryTest.java @@ -9,6 +9,6 @@ import net.sourceforge.pmd.lang.apex.ApexLanguageModule; public class RuleSetFactoryTest extends AbstractRuleSetFactoryTest { public RuleSetFactoryTest() { - super(new ApexLanguageModule()); + super(ApexLanguageModule.TERSE_NAME); } } From fabf2cf6db516fe5c9717be9162f677c969e77f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 24 Nov 2020 11:53:45 +0100 Subject: [PATCH 33/98] Make the parser options properties private --- .../sourceforge/pmd/lang/ParserOptions.java | 18 +++++- .../pmd/lang/ParserOptionsUnitTest.java | 57 ++++++++----------- .../pmd/lang/vf/VfExpressionTypeVisitor.java | 3 +- .../net/sourceforge/pmd/lang/vf/VfParser.java | 2 +- .../pmd/lang/vf/VfParserOptions.java | 4 +- .../pmd/lang/vf/VfParserOptionsTest.java | 4 +- 6 files changed, 45 insertions(+), 43 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java index cd04097ac3..36ec8a6e20 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java @@ -9,7 +9,6 @@ import java.util.Objects; import net.sourceforge.pmd.properties.AbstractPropertySource; import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertySource; /** * Represents a set of configuration options for a {@link Parser}. For each @@ -48,8 +47,21 @@ public class ParserOptions { this.suppressMarker = suppressMarker; } - public PropertySource getProperties() { - return parserOptionsProperties; + protected final void defineProperty(PropertyDescriptor propertyDescriptor) { + parserOptionsProperties.definePropertyDescriptor(propertyDescriptor); + } + + protected final void defineProperty(PropertyDescriptor propertyDescriptor, T initialValue) { + defineProperty(propertyDescriptor); + setProperty(propertyDescriptor, initialValue); + } + + protected final void setProperty(PropertyDescriptor propertyDescriptor, T initialValue) { + parserOptionsProperties.setProperty(propertyDescriptor, initialValue); + } + + public final T getProperty(PropertyDescriptor propertyDescriptor) { + return parserOptionsProperties.getProperty(propertyDescriptor); } @Override diff --git a/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java b/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java index e4b45cc817..fdd357cb08 100644 --- a/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java +++ b/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java @@ -48,8 +48,8 @@ public class ParserOptionsUnitTest { private TestParserOptions() { super(new DummyLanguageModule()); - getProperties().definePropertyDescriptor(LIST_DESCRIPTOR); - getProperties().definePropertyDescriptor(STRING_DESCRIPTOR); + defineProperty(LIST_DESCRIPTOR); + defineProperty(STRING_DESCRIPTOR); overridePropertiesFromEnv(); } } @@ -68,19 +68,18 @@ public class ParserOptionsUnitTest { @Test public void testDefaultPropertyDescriptors() { TestParserOptions parserOptions = new TestParserOptions(); - assertEquals(DEFAULT_LIST, parserOptions.getProperties().getProperty(TestParserOptions.LIST_DESCRIPTOR)); - assertEquals(DEFAULT_STRING, parserOptions.getProperties().getProperty(TestParserOptions.STRING_DESCRIPTOR)); - assertEquals("TestParserOptions", parserOptions.getProperties().getName()); + assertEquals(DEFAULT_LIST, parserOptions.getProperty(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals(DEFAULT_STRING, parserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); } @Test public void testOverriddenPropertyDescriptors() { TestParserOptions parserOptions = new TestParserOptions(); - parserOptions.getProperties().setProperty(TestParserOptions.LIST_DESCRIPTOR, OVERRIDDEN_LIST); - parserOptions.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + parserOptions.setProperty(TestParserOptions.LIST_DESCRIPTOR, OVERRIDDEN_LIST); + parserOptions.setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); - assertEquals(OVERRIDDEN_LIST, parserOptions.getProperties().getProperty(TestParserOptions.LIST_DESCRIPTOR)); - assertEquals(OVERRIDDEN_STRING, parserOptions.getProperties().getProperty(TestParserOptions.STRING_DESCRIPTOR)); + assertEquals(OVERRIDDEN_LIST, parserOptions.getProperty(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals(OVERRIDDEN_STRING, parserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); } @Test @@ -98,8 +97,8 @@ public class ParserOptionsUnitTest { } }; - assertEquals(OVERRIDDEN_LIST, parserOptions.getProperties().getProperty(TestParserOptions.LIST_DESCRIPTOR)); - assertEquals(OVERRIDDEN_STRING, parserOptions.getProperties().getProperty(TestParserOptions.STRING_DESCRIPTOR)); + assertEquals(OVERRIDDEN_LIST, parserOptions.getProperty(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals(OVERRIDDEN_STRING, parserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); } @Test @@ -116,8 +115,8 @@ public class ParserOptionsUnitTest { } }; - assertEquals(Collections.emptyList(), vfParserOptions.getProperties().getProperty(TestParserOptions.LIST_DESCRIPTOR)); - assertEquals("", vfParserOptions.getProperties().getProperty(TestParserOptions.STRING_DESCRIPTOR)); + assertEquals(Collections.emptyList(), vfParserOptions.getProperty(TestParserOptions.LIST_DESCRIPTOR)); + assertEquals("", vfParserOptions.getProperty(TestParserOptions.STRING_DESCRIPTOR)); } /** @@ -147,10 +146,10 @@ public class ParserOptionsUnitTest { options2 = new ParserOptions(); options3 = new ParserOptions(); options4 = new ParserOptions(); - options1.getProperties().definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); - options2.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options3.getProperties().definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); - options4.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); + options1.defineProperty(TestParserOptions.LIST_DESCRIPTOR); + options2.defineProperty(TestParserOptions.STRING_DESCRIPTOR); + options3.defineProperty(TestParserOptions.LIST_DESCRIPTOR); + options4.defineProperty(TestParserOptions.STRING_DESCRIPTOR); ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); // PropertyValue @@ -158,14 +157,10 @@ public class ParserOptionsUnitTest { options2 = new ParserOptions(); options3 = new ParserOptions(); options4 = new ParserOptions(); - options1.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options1.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); - options2.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options2.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); - options3.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options3.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); - options4.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options4.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + options1.defineProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); + options2.defineProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + options3.defineProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); + options4.defineProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); // Language @@ -184,14 +179,10 @@ public class ParserOptionsUnitTest { options2.setSuppressMarker("bar"); options3.setSuppressMarker("foo"); options4.setSuppressMarker("bar"); - options1.getProperties().definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); - options1.getProperties().setProperty(TestParserOptions.LIST_DESCRIPTOR, DEFAULT_LIST); - options2.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options2.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); - options3.getProperties().definePropertyDescriptor(TestParserOptions.LIST_DESCRIPTOR); - options3.getProperties().setProperty(TestParserOptions.LIST_DESCRIPTOR, DEFAULT_LIST); - options4.getProperties().definePropertyDescriptor(TestParserOptions.STRING_DESCRIPTOR); - options4.getProperties().setProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + options1.defineProperty(TestParserOptions.LIST_DESCRIPTOR, DEFAULT_LIST); + options2.defineProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); + options3.defineProperty(TestParserOptions.LIST_DESCRIPTOR, DEFAULT_LIST); + options4.defineProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); assertFalse(options1.equals(null)); diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java index 8c1bf42896..218aa00941 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java @@ -22,7 +22,6 @@ import net.sourceforge.pmd.lang.vf.ast.ASTExpression; import net.sourceforge.pmd.lang.vf.ast.ASTText; import net.sourceforge.pmd.lang.vf.ast.AbstractVFDataNode; import net.sourceforge.pmd.lang.vf.ast.VfParserVisitorAdapter; -import net.sourceforge.pmd.properties.PropertySource; /** * Visits {@link ASTExpression} nodes and stores type information for @@ -51,7 +50,7 @@ class VfExpressionTypeVisitor extends VfParserVisitorAdapter { private final List apexDirectories; private final List objectsDirectories; - VfExpressionTypeVisitor(String fileName, PropertySource propertySource) { + VfExpressionTypeVisitor(String fileName, VfParserOptions propertySource) { this.fileName = fileName; this.apexDirectories = propertySource.getProperty(VfParserOptions.APEX_DIRECTORIES_DESCRIPTOR); this.objectsDirectories = propertySource.getProperty(VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR); diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java index 94d5cdf281..0fec1232cb 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParser.java @@ -47,7 +47,7 @@ public class VfParser extends AbstractParser { ASTCompilationUnit astCompilationUnit = new net.sourceforge.pmd.lang.vf.ast.VfParser( new SimpleCharStream(source)).CompilationUnit(); // Add type information to the AST - VfExpressionTypeVisitor visitor = new VfExpressionTypeVisitor(fileName, this.getParserOptions().getProperties()); + VfExpressionTypeVisitor visitor = new VfExpressionTypeVisitor(fileName, (VfParserOptions) this.getParserOptions()); visitor.visit(astCompilationUnit, null); return astCompilationUnit; diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java index 451d1d365a..61da1f39eb 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java @@ -38,8 +38,8 @@ public class VfParserOptions extends ParserOptions { public VfParserOptions() { super(new VfLanguageModule()); - getProperties().definePropertyDescriptor(APEX_DIRECTORIES_DESCRIPTOR); - getProperties().definePropertyDescriptor(OBJECTS_DIRECTORIES_DESCRIPTOR); + defineProperty(APEX_DIRECTORIES_DESCRIPTOR); + defineProperty(OBJECTS_DIRECTORIES_DESCRIPTOR); overridePropertiesFromEnv(); } } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java index 0a34e45fe4..dc124f19fd 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java @@ -13,8 +13,8 @@ public class VfParserOptionsTest { public void testDefaultPropertyDescriptors() { VfParserOptions vfParserOptions = new VfParserOptions(); assertEquals(VfParserOptions.DEFAULT_APEX_DIRECTORIES, - vfParserOptions.getProperties().getProperty(VfParserOptions.APEX_DIRECTORIES_DESCRIPTOR)); + vfParserOptions.getProperty(VfParserOptions.APEX_DIRECTORIES_DESCRIPTOR)); assertEquals(VfParserOptions.DEFAULT_OBJECT_DIRECTORIES, - vfParserOptions.getProperties().getProperty(VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR)); + vfParserOptions.getProperty(VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR)); } } From 58cf6cbc96f3afb0960b1798b39d6040b62d46db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 24 Nov 2020 11:55:09 +0100 Subject: [PATCH 34/98] Remove AbstractPropertySource equals/hashCode --- .../pmd/properties/AbstractPropertySource.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java index 1a1bc05333..855a232e22 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java @@ -11,7 +11,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Objects; import java.util.Set; import net.sourceforge.pmd.util.CollectionUtil; @@ -254,21 +253,4 @@ public abstract class AbstractPropertySource implements PropertySource { return descriptor.errorFor(getProperty(descriptor)); } - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final AbstractPropertySource that = (AbstractPropertySource) obj; - return Objects.equals(propertyDescriptors, that.propertyDescriptors) - && Objects.equals(propertyValuesByDescriptor, that.propertyValuesByDescriptor); - } - - @Override - public int hashCode() { - return Objects.hash(propertyDescriptors, propertyValuesByDescriptor); - } } From 0ad1f47b29e7ef5683729530cc434fa07623320f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 24 Nov 2020 11:59:43 +0100 Subject: [PATCH 35/98] Use interface instead of deprecated abstract class --- .../pmd/lang/vf/VfExpressionTypeVisitor.java | 11 +++++---- .../pmd/lang/vf/ast/ASTExpression.java | 8 +++---- .../pmd/lang/vf/ast/AbstractVFDataNode.java | 16 ++++--------- .../pmd/lang/vf/ast/VfAstInternals.java | 23 +++++++++++++++++++ .../pmd/lang/vf/ast/VfTypedNode.java | 22 ++++++++++++++++++ .../vf/rule/security/VfUnescapeElRule.java | 4 ++-- .../lang/vf/VfExpressionTypeVisitorTest.java | 10 ++++---- .../pmd/lang/vf/ast/ASTExpressionTest.java | 14 +++++------ 8 files changed, 73 insertions(+), 35 deletions(-) create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfAstInternals.java create mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfTypedNode.java diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java index 218aa00941..8b4db528cd 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor.java @@ -20,8 +20,9 @@ import net.sourceforge.pmd.lang.vf.ast.ASTElExpression; import net.sourceforge.pmd.lang.vf.ast.ASTElement; import net.sourceforge.pmd.lang.vf.ast.ASTExpression; import net.sourceforge.pmd.lang.vf.ast.ASTText; -import net.sourceforge.pmd.lang.vf.ast.AbstractVFDataNode; +import net.sourceforge.pmd.lang.vf.ast.VfAstInternals; import net.sourceforge.pmd.lang.vf.ast.VfParserVisitorAdapter; +import net.sourceforge.pmd.lang.vf.ast.VfTypedNode; /** * Visits {@link ASTExpression} nodes and stores type information for @@ -109,7 +110,7 @@ class VfExpressionTypeVisitor extends VfParserVisitorAdapter { */ @Override public Object visit(ASTElExpression node, Object data) { - for (Map.Entry entry : getDataNodeNames(node).entrySet()) { + for (Map.Entry entry : getDataNodeNames(node).entrySet()) { String name = entry.getValue(); DataType type = null; String[] parts = name.split("\\."); @@ -152,7 +153,7 @@ class VfExpressionTypeVisitor extends VfParserVisitorAdapter { } if (type != null) { - entry.getKey().setDataType(type); + VfAstInternals.setDataType(entry.getKey(), type); } else { LOGGER.fine("Unable to determine type for: " + name); } @@ -164,8 +165,8 @@ class VfExpressionTypeVisitor extends VfParserVisitorAdapter { * Invoke {@link ASTExpression#getDataNodes()} for all {@link ASTExpression} children of {@code node} and return * the consolidated results. */ - private IdentityHashMap getDataNodeNames(ASTElExpression node) { - IdentityHashMap dataNodeToName = new IdentityHashMap<>(); + private IdentityHashMap getDataNodeNames(ASTElExpression node) { + IdentityHashMap dataNodeToName = new IdentityHashMap<>(); for (ASTExpression expression : node.findChildrenOfType(ASTExpression.class)) { try { diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTExpression.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTExpression.java index 39977ce1f0..01cfb488a3 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTExpression.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ASTExpression.java @@ -102,13 +102,13 @@ public class ASTExpression extends AbstractVFNode { * @throws DataNodeStateException if the results of this method could have been incorrect. Callers should typically * not rethrow this exception, as it will happen often and doesn't represent a terminal exception. */ - public Map getDataNodes() throws DataNodeStateException { - Map result = new IdentityHashMap<>(); + public Map getDataNodes() throws DataNodeStateException { + Map result = new IdentityHashMap<>(); int numChildren = getNumChildren(); List identifiers = findChildrenOfType(ASTIdentifier.class); for (ASTIdentifier identifier : identifiers) { - LinkedList identifierNodes = new LinkedList<>(); + LinkedList identifierNodes = new LinkedList<>(); // The Identifier is the first item that makes up the string identifierNodes.add(identifier); @@ -126,7 +126,7 @@ public class ASTExpression extends AbstractVFNode { if (node.getNumChildren() == 1) { final Node expressionChild = node.getChild(0); if (expressionChild instanceof ASTIdentifier || expressionChild instanceof ASTLiteral) { - identifierNodes.add((AbstractVFDataNode) expressionChild); + identifierNodes.add((VfTypedNode) expressionChild); } else { // This should never happen logWarning("Node expected to be Identifier or Literal", node); diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java index 280cadb7ef..d686402a67 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java @@ -4,15 +4,13 @@ package net.sourceforge.pmd.lang.vf.ast; -import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.vf.DataType; /** * Represents a node that displays a piece of data. */ -@Deprecated -@InternalApi -public class AbstractVFDataNode extends AbstractVFNode { +class AbstractVFDataNode extends AbstractVFNode implements VfTypedNode { + private DataType dataType; public AbstractVFDataNode(int id) { @@ -24,18 +22,12 @@ public class AbstractVFDataNode extends AbstractVFNode { this.parser = parser; } - /** - * Example XPath 1.0 and 2.0: {@code //Identifier[@DataType='DateTime']} - * - * @return data type that this node refers to. A null value indicates that no matching Metadata was found for this - * node. null differs from {@link DataType#Unknown} which indicates that Metadata was found but it wasn't mappable - * to one of the enums. - */ + @Override public DataType getDataType() { return dataType; } - public void setDataType(DataType dataType) { + void setDataType(DataType dataType) { this.dataType = dataType; } } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfAstInternals.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfAstInternals.java new file mode 100644 index 0000000000..e45aae08a2 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfAstInternals.java @@ -0,0 +1,23 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.vf.DataType; + +/** + * This is internal API, and can be changed at any time. + */ +@InternalApi +public final class VfAstInternals { + + private VfAstInternals() { + // utility class + } + + public static void setDataType(VfTypedNode node, DataType dataType) { + ((AbstractVFDataNode) node).setDataType(dataType); + } +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfTypedNode.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfTypedNode.java new file mode 100644 index 0000000000..8d0d74b6d1 --- /dev/null +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfTypedNode.java @@ -0,0 +1,22 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.vf.ast; + +import net.sourceforge.pmd.lang.vf.DataType; + +/** + * Represents a node that displays a piece of data. + */ +public interface VfTypedNode extends VfNode { + + /** + * Returns the data type this node refers to. A null value indicates that no matching Metadata was found for this + * node. null differs from {@link DataType#Unknown} which indicates that Metadata was found but it wasn't mappable + * to one of the enums. + * + *

Example XPath 1.0 and 2.0: {@code //Identifier[@DataType='DateTime']} + */ + DataType getDataType(); +} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java index d41c086127..77527770e5 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/rule/security/VfUnescapeElRule.java @@ -25,8 +25,8 @@ import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; import net.sourceforge.pmd.lang.vf.ast.ASTLiteral; import net.sourceforge.pmd.lang.vf.ast.ASTNegationExpression; import net.sourceforge.pmd.lang.vf.ast.ASTText; -import net.sourceforge.pmd.lang.vf.ast.AbstractVFDataNode; import net.sourceforge.pmd.lang.vf.ast.AbstractVFNode; +import net.sourceforge.pmd.lang.vf.ast.VfTypedNode; import net.sourceforge.pmd.lang.vf.rule.AbstractVfRule; /** @@ -451,7 +451,7 @@ public class VfUnescapeElRule extends AbstractVfRule { */ private boolean expressionContainsSafeDataNodes(ASTExpression expression) { try { - for (AbstractVFDataNode node : expression.getDataNodes().keySet()) { + for (VfTypedNode node : expression.getDataNodes().keySet()) { DataType dataType = node.getDataType(); if (dataType == null || dataType.requiresEscaping) { return false; diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitorTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitorTest.java index 0997a9695e..f670dba54b 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitorTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitorTest.java @@ -26,7 +26,7 @@ import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.vf.ast.ASTIdentifier; import net.sourceforge.pmd.lang.vf.ast.ASTLiteral; -import net.sourceforge.pmd.lang.vf.ast.AbstractVFDataNode; +import net.sourceforge.pmd.lang.vf.ast.VfTypedNode; import net.sourceforge.pmd.util.treeexport.XmlTreeRenderer; public class VfExpressionTypeVisitorTest { @@ -110,8 +110,8 @@ public class VfExpressionTypeVisitorTest { // Each string appears twice, it is set on a "value" attribute and inline assertEquals(2, nodes.size()); for (Node node : nodes) { - assertTrue(node.getClass().getSimpleName(), node instanceof AbstractVFDataNode); - AbstractVFDataNode dataNode = (AbstractVFDataNode) node; + assertTrue(node.getClass().getSimpleName(), node instanceof VfTypedNode); + VfTypedNode dataNode = (VfTypedNode) node; assertNull(dataNode.getDataType()); } } @@ -150,8 +150,8 @@ public class VfExpressionTypeVisitorTest { // Each string appears twice, it is set on a "value" attribute and inline assertEquals(2, nodes.size()); for (Node node : nodes) { - assertTrue(node.getClass().getSimpleName(), node instanceof AbstractVFDataNode); - AbstractVFDataNode dataNode = (AbstractVFDataNode) node; + assertTrue(node.getClass().getSimpleName(), node instanceof VfTypedNode); + VfTypedNode dataNode = (VfTypedNode) node; assertNull(dataNode.getDataType()); } } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ASTExpressionTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ASTExpressionTest.java index d432b1b8e3..135c46f443 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ASTExpressionTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ASTExpressionTest.java @@ -39,7 +39,7 @@ public class ASTExpressionTest { assertEquals(template, 1, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); - Map identifiers = expression.getDataNodes(); + Map identifiers = expression.getDataNodes(); assertEquals(template, 1, identifiers.size()); Map map = invertMap(identifiers); @@ -57,7 +57,7 @@ public class ASTExpressionTest { assertEquals(template, 1, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); - Map identifiers = expression.getDataNodes(); + Map identifiers = expression.getDataNodes(); assertEquals(template, 1, identifiers.size()); Map map = invertMap(identifiers); @@ -75,7 +75,7 @@ public class ASTExpressionTest { assertEquals(template, 1, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); - Map identifiers = expression.getDataNodes(); + Map identifiers = expression.getDataNodes(); assertEquals(template, 1, identifiers.size()); Map map = invertMap(identifiers); @@ -93,7 +93,7 @@ public class ASTExpressionTest { assertEquals(template, 1, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); - Map identifiers = expression.getDataNodes(); + Map identifiers = expression.getDataNodes(); assertEquals(template, 2, identifiers.size()); Map map = invertMap(identifiers); @@ -114,7 +114,7 @@ public class ASTExpressionTest { assertEquals(template, 1, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); - Map identifiers = expression.getDataNodes(); + Map identifiers = expression.getDataNodes(); assertEquals(template, 1, identifiers.size()); Map map = invertMap(identifiers); @@ -133,7 +133,7 @@ public class ASTExpressionTest { assertEquals(template, 1, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); - Map identifiers = expression.getDataNodes(); + Map identifiers = expression.getDataNodes(); assertEquals(template, 2, identifiers.size()); Map map = invertMap(identifiers); @@ -206,7 +206,7 @@ public class ASTExpressionTest { /** * Invert the map to make it easier to unit test. */ - private Map invertMap(Map map) { + private Map invertMap(Map map) { Map result = map.entrySet().stream() .collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); // Ensure no values have been lost From b94ac4f0180a476ed5f883d987f42cd61371c0ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 24 Nov 2020 12:09:25 +0100 Subject: [PATCH 36/98] Don't create a vf language module per parser options --- .../net/sourceforge/pmd/lang/ParserOptions.java | 14 +++++++------- .../sourceforge/pmd/lang/vf/VfParserOptions.java | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java index 36ec8a6e20..e33d0f9caf 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java @@ -26,7 +26,7 @@ public class ParserOptions { /** * Language used to construct environment variable names that match PropertyDescriptors. */ - private final Language language; + private final String languageId; private final ParserOptionsProperties parserOptionsProperties; @@ -34,8 +34,8 @@ public class ParserOptions { this(null); } - public ParserOptions(Language language) { - this.language = language; + public ParserOptions(String languageId) { + this.languageId = languageId; this.parserOptionsProperties = new ParserOptionsProperties(); } @@ -74,23 +74,23 @@ public class ParserOptions { } final ParserOptions that = (ParserOptions) obj; return Objects.equals(suppressMarker, that.suppressMarker) - && Objects.equals(language, that.language) + && Objects.equals(languageId, that.languageId) && Objects.equals(parserOptionsProperties, that.parserOptionsProperties); } @Override public int hashCode() { - return Objects.hash(suppressMarker, language, parserOptionsProperties); + return Objects.hash(suppressMarker, languageId, parserOptionsProperties); } /** * Returns the environment variable name that a user can set in order to override the default value. */ String getEnvironmentVariableName(PropertyDescriptor propertyDescriptor) { - if (language == null) { + if (languageId == null) { throw new IllegalStateException("Language is null"); } - return "PMD_" + language.getTerseName().toUpperCase(Locale.ROOT) + "_" + return "PMD_" + languageId.toUpperCase(Locale.ROOT) + "_" + propertyDescriptor.name().toUpperCase(Locale.ROOT); } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java index 61da1f39eb..c69be0a95b 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java @@ -37,7 +37,7 @@ public class VfParserOptions extends ParserOptions { .build(); public VfParserOptions() { - super(new VfLanguageModule()); + super(VfLanguageModule.TERSE_NAME); defineProperty(APEX_DIRECTORIES_DESCRIPTOR); defineProperty(OBJECTS_DIRECTORIES_DESCRIPTOR); overridePropertiesFromEnv(); From 4efe1493a597ba012ad9f6d0d3361928c9952096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 24 Nov 2020 12:12:32 +0100 Subject: [PATCH 37/98] Remove some unchecked warnings --- .../sourceforge/pmd/lang/ParserOptions.java | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java index e33d0f9caf..a5e0e88b3f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java @@ -86,19 +86,19 @@ public class ParserOptions { /** * Returns the environment variable name that a user can set in order to override the default value. */ - String getEnvironmentVariableName(PropertyDescriptor propertyDescriptor) { + String getEnvironmentVariableName(PropertyDescriptor propertyDescriptor) { if (languageId == null) { throw new IllegalStateException("Language is null"); } return "PMD_" + languageId.toUpperCase(Locale.ROOT) + "_" - + propertyDescriptor.name().toUpperCase(Locale.ROOT); + + propertyDescriptor.name().toUpperCase(Locale.ROOT); } /** * @return environment variable that overrides the PropertyDesciptors default value. Returns null if no environment - * variable has been set. + * variable has been set. */ - protected String getEnvValue(PropertyDescriptor propertyDescriptor) { + protected String getEnvValue(PropertyDescriptor propertyDescriptor) { return System.getenv(getEnvironmentVariableName(propertyDescriptor)); } @@ -107,17 +107,22 @@ public class ParserOptions { * TODO: Move this to net.sourceforge.pmd.PMD#parserFor when CLI options are implemented */ protected void overridePropertiesFromEnv() { - for (PropertyDescriptor propertyDescriptor : parserOptionsProperties.getPropertyDescriptors()) { + for (PropertyDescriptor propertyDescriptor : parserOptionsProperties.getPropertyDescriptors()) { String propertyValue = getEnvValue(propertyDescriptor); if (propertyValue != null) { - Object value = propertyDescriptor.valueFrom(propertyValue); - parserOptionsProperties.setProperty(propertyDescriptor, value); + setPropertyCapture(propertyDescriptor, propertyValue); } } } + private void setPropertyCapture(PropertyDescriptor propertyDescriptor, String propertyValue) { + T value = propertyDescriptor.valueFrom(propertyValue); + parserOptionsProperties.setProperty(propertyDescriptor, value); + } + private final class ParserOptionsProperties extends AbstractPropertySource { + @Override protected String getPropertySourceType() { return "ParserOptions"; @@ -127,5 +132,23 @@ public class ParserOptions { public String getName() { return ParserOptions.this.getClass().getSimpleName(); } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ParserOptionsProperties)) { + return false; + } + final ParserOptionsProperties that = (ParserOptionsProperties) obj; + return Objects.equals(getPropertiesByPropertyDescriptor(), + that.getPropertiesByPropertyDescriptor()); + } + + @Override + public int hashCode() { + return getPropertiesByPropertyDescriptor().hashCode(); + } } } From 2d5781482eddbd67568c01da584305fa117a794e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 24 Nov 2020 12:17:52 +0100 Subject: [PATCH 38/98] Fix tests --- .../sourceforge/pmd/lang/ParserOptionsUnitTest.java | 12 ++++++------ .../pmd/lang/vf/ast/AbstractVFDataNode.java | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java b/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java index fdd357cb08..239a22df89 100644 --- a/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java +++ b/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java @@ -47,7 +47,7 @@ public class ParserOptionsUnitTest { .build(); private TestParserOptions() { - super(new DummyLanguageModule()); + super(DummyLanguageModule.TERSE_NAME); defineProperty(LIST_DESCRIPTOR); defineProperty(STRING_DESCRIPTOR); overridePropertiesFromEnv(); @@ -157,23 +157,23 @@ public class ParserOptionsUnitTest { options2 = new ParserOptions(); options3 = new ParserOptions(); options4 = new ParserOptions(); - options1.defineProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); + options1.defineProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); options2.defineProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); options3.defineProperty(TestParserOptions.STRING_DESCRIPTOR, DEFAULT_STRING); options4.defineProperty(TestParserOptions.STRING_DESCRIPTOR, OVERRIDDEN_STRING); ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); // Language - options1 = new ParserOptions(new DummyLanguageModule()); + options1 = new ParserOptions(DummyLanguageModule.TERSE_NAME); options2 = new ParserOptions(); - options3 = new ParserOptions(new DummyLanguageModule()); + options3 = new ParserOptions(DummyLanguageModule.TERSE_NAME); options4 = new ParserOptions(); ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); // SuppressMarker, PropertyDescriptor, PropertyValue, Language - options1 = new ParserOptions(new DummyLanguageModule()); + options1 = new ParserOptions(DummyLanguageModule.TERSE_NAME); options2 = new ParserOptions(); - options3 = new ParserOptions(new DummyLanguageModule()); + options3 = new ParserOptions(DummyLanguageModule.TERSE_NAME); options4 = new ParserOptions(); options1.setSuppressMarker("foo"); options2.setSuppressMarker("bar"); diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java index d686402a67..88c65928b6 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/AbstractVFDataNode.java @@ -13,11 +13,11 @@ class AbstractVFDataNode extends AbstractVFNode implements VfTypedNode { private DataType dataType; - public AbstractVFDataNode(int id) { + AbstractVFDataNode(int id) { super(id); } - public AbstractVFDataNode(VfParser parser, int id) { + AbstractVFDataNode(VfParser parser, int id) { super(id); this.parser = parser; } From 87e2a9c99b2b546c99ad776ddf3754053adb3be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 24 Nov 2020 13:05:21 +0100 Subject: [PATCH 39/98] Replace checked exception with wrapper --- .../net/sourceforge/pmd/RuleSetLoader.java | 68 ++++++++++++------- .../pmd/RuleSetNotFoundException.java | 6 ++ .../sourceforge/pmd/RulesetLoadException.java | 32 +++++++++ 3 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/RulesetLoadException.java 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 032372ae7c..53e65a8cba 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -7,21 +7,22 @@ package net.sourceforge.pmd; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Properties; +import java.util.logging.Level; import java.util.logging.Logger; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.util.CollectionUtil; import net.sourceforge.pmd.util.ResourceLoader; /** - * Configurable ruleset parser. Note that this replaces the API of {@link RulesetsFactoryUtils} - * and {@link RuleSetFactory}. This can be configured using a fluent - * API, see eg {@link #warnDeprecated(boolean)}. To create a list of - * rulesets, use {@link #loadFromResource(String)}. + * Configurable object to load rulesets from XML resources. + * This can be configured using a fluent API, see eg {@link #warnDeprecated(boolean)}. + * To create a new ruleset, use {@link #loadFromResource(String)} + * or some such overload. */ public final class RuleSetLoader { @@ -98,8 +99,8 @@ public final class RuleSetLoader { * Create a new rule set factory, if you have to (that class is deprecated). * That factory will use the configuration that was set using the setters of this. * - * @deprecated {@link RuleSetFactory} is deprecated, replace its usages with usages of this class, - * or of static factory methods of {@link RuleSet} + * @deprecated {@link RuleSetFactory} is deprecated, replace its usages + * with usages of this class, or of static factory methods of {@link RuleSet} */ @Deprecated public RuleSetFactory toFactory() { @@ -122,9 +123,9 @@ public final class RuleSetLoader { * * @param rulesetPath A reference to a single ruleset * - * @throws RuleSetNotFoundException If the path does not correspond to a resource + * @throws RulesetLoadException If any error occurs (eg, invalid syntax, or resource not found) */ - public RuleSet loadFromResource(String rulesetPath) throws RuleSetNotFoundException { + public RuleSet loadFromResource(String rulesetPath) { return loadFromResource(new RuleSetReferenceId(rulesetPath)); } @@ -133,10 +134,11 @@ public final class RuleSetLoader { * * @param paths Paths * - * @throws RuleSetNotFoundException If any resource throws - * @throws NullPointerException If the parameter, or any component is null + * @throws RulesetLoadException If any error occurs (eg, invalid syntax, or resource not found), + * for any of the parameters + * @throws NullPointerException If the parameter, or any component is null */ - public List loadFromResources(Collection paths) throws RuleSetNotFoundException { + public List loadFromResources(Collection paths) { List ruleSets = new ArrayList<>(paths.size()); for (String path : paths) { ruleSets.add(loadFromResource(path)); @@ -147,18 +149,24 @@ public final class RuleSetLoader { /** * Parses several resources into a list of rulesets. * - * @param paths Paths + * @param first First path + * @param rest Paths * - * @throws RuleSetNotFoundException If any resource throws - * @throws NullPointerException If the parameter, or any component is null + * @throws RulesetLoadException If any error occurs (eg, invalid syntax, or resource not found), + * for any of the parameters + * @throws NullPointerException If the parameter, or any component is null */ - public List loadFromResources(String... paths) throws RuleSetNotFoundException { - return loadFromResources(Arrays.asList(paths)); + public List loadFromResources(String first, String... rest) { + return loadFromResources(CollectionUtil.listOf(first, rest)); } // package private - RuleSet loadFromResource(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { - return toFactory().createRuleSet(ruleSetReferenceId); + RuleSet loadFromResource(RuleSetReferenceId ruleSetReferenceId) { + try { + return toFactory().createRuleSet(ruleSetReferenceId); + } catch (Exception e) { + throw new RulesetLoadException("Cannot parse " + ruleSetReferenceId, e); + } } @@ -182,7 +190,7 @@ public final class RuleSetLoader { * @throws RuleSetNotFoundException if some ruleset file could not be parsed * TODO shouldn't our API forbid this case? */ - public List getStandardRuleSets() throws RuleSetNotFoundException { + public List getStandardRuleSets() { String rulesetsProperties; List ruleSetReferenceIds = new ArrayList<>(); for (Language language : LanguageRegistry.findWithRuleSupport()) { @@ -195,13 +203,23 @@ public final class RuleSetLoader { ruleSetReferenceIds.addAll(RuleSetReferenceId.parse(rulesetFilenames)); } } catch (RuleSetNotFoundException e) { - LOG.fine("The language " + language.getTerseName() + " provides no " + rulesetsProperties + "."); + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("The language " + language.getTerseName() + " provides no " + rulesetsProperties + "."); + } } catch (IOException ioe) { - throw new RuleSetNotFoundException("Couldn't read " + rulesetsProperties - + "; please ensure that the directory is on the classpath. The current classpath is: " - + System.getProperty("java.class.path"), ioe); + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Couldn't read " + rulesetsProperties + + "; please ensure that the directory is on the classpath. The current classpath is: " + + System.getProperty("java.class.path")); + LOG.fine(ioe.toString()); + } } } - return toFactory().createRuleSets(ruleSetReferenceIds).getRuleSetsInternal(); + + List ruleSets = new ArrayList<>(); + for (RuleSetReferenceId id : ruleSetReferenceIds) { + ruleSets.add(loadFromResource(id)); // may throw + } + return ruleSets; } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java index 5c86702518..05dd3d4be2 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java @@ -4,7 +4,13 @@ package net.sourceforge.pmd; +/** + * @deprecated This is now only thrown by deprecated apis. {@link RuleSetLoader} + * throws {@link RulesetLoadException} instead + */ +@Deprecated public class RuleSetNotFoundException extends Exception { + private static final long serialVersionUID = -4617033110919250810L; public RuleSetNotFoundException(String msg) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetLoadException.java b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetLoadException.java new file mode 100644 index 0000000000..7979971118 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetLoadException.java @@ -0,0 +1,32 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd; + +import net.sourceforge.pmd.annotation.InternalApi; + +/** + * An exception that is thrown when something wrong occurs while + * {@linkplain RuleSetLoader loading rulesets}. This may be because the + * XML is not well-formed, does not respect the ruleset schema, is + * not a valid ruleset or is otherwise unparsable. + * + *

In the new {@link RuleSetLoader} API, this is thrown instead of + * {@link RuleSetNotFoundException}. + */ +public final class RulesetLoadException extends RuntimeException { + + /** Constructors are internal. */ + @InternalApi + public RulesetLoadException(String message, Throwable cause) { + super(message, cause); + } + + /** Constructors are internal. */ + @InternalApi + public RulesetLoadException(String message) { + super(message); + } + +} From b0df6a82480a91c28902420befed86d9db237b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 24 Nov 2020 13:18:30 +0100 Subject: [PATCH 40/98] Update pmd-doc module to use newer apis --- .../pmd/docs/GenerateRuleDocsCmd.java | 11 +--- .../pmd/docs/RuleDocGenerator.java | 61 +++++++------------ 2 files changed, 24 insertions(+), 48 deletions(-) diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java index 2256a21d18..1658262fb3 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/GenerateRuleDocsCmd.java @@ -13,24 +13,20 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; -import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.RulesetsFactoryUtils; +import net.sourceforge.pmd.RuleSetLoader; public final class GenerateRuleDocsCmd { private GenerateRuleDocsCmd() { // Utility class } - public static void main(String[] args) throws RuleSetNotFoundException { + public static void main(String[] args) throws IOException { if (args.length != 1) { System.err.println("One argument is required: The base directory of the module pmd-doc."); System.exit(1); @@ -41,8 +37,7 @@ public final class GenerateRuleDocsCmd { System.out.println("Generating docs into " + output); // important: use a RuleSetFactory that includes all rules, e.g. deprecated rule references - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.createFactory(RulePriority.LOW, false, false, true); - Iterator registeredRuleSets = ruleSetFactory.getRegisteredRuleSets(); + List registeredRuleSets = new RuleSetLoader().getStandardRuleSets(); List additionalRulesets = findAdditionalRulesets(output); RuleDocGenerator generator = new RuleDocGenerator(new DefaultFileWriter(), output); diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java index f62400b1a9..2b5d83c0cc 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java @@ -17,7 +17,6 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -29,6 +28,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; @@ -36,9 +36,8 @@ import org.apache.commons.text.StringEscapeUtils; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.RulesetsFactoryUtils; +import net.sourceforge.pmd.RuleSetLoader; +import net.sourceforge.pmd.RulesetLoadException; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.lang.rule.XPathRule; @@ -91,21 +90,16 @@ public class RuleDocGenerator { } } - public void generate(Iterator registeredRulesets, List additionalRulesets) { + public void generate(List registeredRulesets, List additionalRulesets) throws IOException { Map> sortedRulesets; Map> sortedAdditionalRulesets; - try { - sortedRulesets = sortRulesets(registeredRulesets); - sortedAdditionalRulesets = sortRulesets(resolveAdditionalRulesets(additionalRulesets)); - determineRuleClassSourceFiles(sortedRulesets); - generateLanguageIndex(sortedRulesets, sortedAdditionalRulesets); - generateRuleSetIndex(sortedRulesets); + sortedRulesets = sortRulesets(registeredRulesets); + sortedAdditionalRulesets = sortRulesets(resolveAdditionalRulesets(additionalRulesets)); + determineRuleClassSourceFiles(sortedRulesets); + generateLanguageIndex(sortedRulesets, sortedAdditionalRulesets); + generateRuleSetIndex(sortedRulesets); - generateSidebar(sortedRulesets); - - } catch (RuleSetNotFoundException | IOException e) { - throw new RuntimeException(e); - } + generateSidebar(sortedRulesets); } private void generateSidebar(Map> sortedRulesets) throws IOException { @@ -113,53 +107,40 @@ public class RuleDocGenerator { generator.generateSidebar(sortedRulesets); } - private Iterator resolveAdditionalRulesets(List additionalRulesets) throws RuleSetNotFoundException { + private List resolveAdditionalRulesets(List additionalRulesets) { if (additionalRulesets == null) { - return Collections.emptyIterator(); + return Collections.emptyList(); } List rulesets = new ArrayList<>(); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); + RuleSetLoader ruleSetLoader = new RuleSetLoader(); for (String filename : additionalRulesets) { try { // do not take rulesets from pmd-test or pmd-core if (!filename.contains("pmd-test") && !filename.contains("pmd-core")) { - rulesets.add(ruleSetFactory.createRuleSet(filename)); + rulesets.add(ruleSetLoader.loadFromResource(filename)); } else { LOG.fine("Ignoring ruleset " + filename); } - } catch (IllegalArgumentException e) { + } catch (RulesetLoadException e) { // ignore rulesets, we can't read LOG.log(Level.WARNING, "ruleset file " + filename + " ignored (" + e.getMessage() + ")", e); } } - return rulesets.iterator(); + return rulesets; } private Path getAbsoluteOutputPath(String filename) { return root.resolve(FilenameUtils.normalize(filename)); } - private Map> sortRulesets(Iterator rulesets) throws RuleSetNotFoundException { - SortedMap> rulesetsByLanguage = new TreeMap<>(); - - while (rulesets.hasNext()) { - RuleSet ruleset = rulesets.next(); - Language language = getRuleSetLanguage(ruleset); - - if (!rulesetsByLanguage.containsKey(language)) { - rulesetsByLanguage.put(language, new ArrayList()); - } - rulesetsByLanguage.get(language).add(ruleset); - } + private Map> sortRulesets(List rulesets) { + SortedMap> rulesetsByLanguage = rulesets.stream().collect(Collectors.groupingBy(RuleDocGenerator::getRuleSetLanguage, + TreeMap::new, + Collectors.toCollection(ArrayList::new))); for (List rulesetsOfOneLanguage : rulesetsByLanguage.values()) { - Collections.sort(rulesetsOfOneLanguage, new Comparator() { - @Override - public int compare(RuleSet o1, RuleSet o2) { - return o1.getName().compareToIgnoreCase(o2.getName()); - } - }); + rulesetsOfOneLanguage.sort((o1, o2) -> o1.getName().compareToIgnoreCase(o2.getName())); } return rulesetsByLanguage; } From 1a2a897b3f37f2263fd44acaea52d8990560bd82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 24 Nov 2020 13:55:26 +0100 Subject: [PATCH 41/98] Fix bug with sub-report not being merged into global report --- .../main/java/net/sourceforge/pmd/Report.java | 26 ++++++++++++------- .../pmd/processor/PmdRunnable.java | 3 +++ 2 files changed, 19 insertions(+), 10 deletions(-) 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 f0a75a7028..8bafa2ccc5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/Report.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/Report.java @@ -47,6 +47,7 @@ public class Report implements Iterable { private final List listeners = new ArrayList<>(); private final List errors = new ArrayList<>(); private final List configErrors = new ArrayList<>(); + private final Object lock = new Object(); private Map linesToSuppress = new HashMap<>(); private long start; private long end; @@ -394,20 +395,25 @@ public class Report implements Iterable { * summary over all violations is needed as PMD creates one report per file * by default. * - * @param r - * the report to be merged into this. + *

This is synchronized on an internal lock (note that other mutation + * operations are not synchronized, todo for pmd 7). + * + * @param r the report to be merged into this. + * * @see AbstractAccumulatingRenderer */ public void merge(Report r) { - errors.addAll(r.errors); - configErrors.addAll(r.configErrors); - metrics.addAll(r.metrics); - suppressedRuleViolations.addAll(r.suppressedRuleViolations); + synchronized (lock) { + errors.addAll(r.errors); + configErrors.addAll(r.configErrors); + metrics.addAll(r.metrics); + suppressedRuleViolations.addAll(r.suppressedRuleViolations); - for (RuleViolation violation : r.getViolations()) { - int index = Collections.binarySearch(violations, violation, RuleViolation.DEFAULT_COMPARATOR); - violations.add(index < 0 ? -index - 1 : index, violation); - violationTree.addRuleViolation(violation); + for (RuleViolation violation : r.getViolations()) { + int index = Collections.binarySearch(violations, violation, RuleViolation.DEFAULT_COMPARATOR); + violations.add(index < 0 ? -index - 1 : index, violation); + violationTree.addRuleViolation(violation); + } } } 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 4c290a61b6..da91fbd95a 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 @@ -93,6 +93,9 @@ public class PmdRunnable implements Callable { TimeTracker.finishThread(); + // merge the sub-report into the global report (thread-safe) + ruleContext.getReport().merge(report); + return report; } From 141c51b0ab903fe079a21a76ac193c8b0f8ebd84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 24 Nov 2020 14:34:35 +0100 Subject: [PATCH 42/98] Fix ant tests Report was being rendered mutliple times --- pmd-core/src/main/java/net/sourceforge/pmd/PMD.java | 2 +- .../sourceforge/pmd/ant/internal/PMDTaskImpl.java | 12 ++++-------- .../pmd/processor/AbstractPMDProcessor.java | 7 ++++--- .../java/net/sourceforge/pmd/ant/PMDTaskTest.java | 3 +-- .../sourceforge/pmd/docs/RuleDocGeneratorTest.java | 2 +- 5 files changed, 11 insertions(+), 15 deletions(-) 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 47619f4844..88e66e2d53 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -333,7 +333,7 @@ public class PMD { * and rulesets, are ignored, as they are supplied * as parameters * @param rulesets Parsed rulesets - * @param files Files to process + * @param files Files to process, will be closed by this method. * @param renderers Renderers that render the report * * @return Report in which violations are accumulated 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 685c459bd9..6fe78b9856 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 @@ -12,7 +12,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ContextedRuntimeException; @@ -132,7 +131,7 @@ public class PMDTaskImpl { // TODO Do we really need all this in a loop over each FileSet? Seems // like a lot of redundancy Report errorReport = new Report(); - final AtomicInteger reportSize = new AtomicInteger(); + int problemCount = 0; final String separator = System.getProperty("file.separator"); for (FileSet fs : filesets) { @@ -165,10 +164,7 @@ public class PMDTaskImpl { @Override public void renderFileReport(Report r) { - int size = r.size(); - if (size > 0) { - reportSize.addAndGet(size); - } + // Nothing to do } @Override @@ -189,7 +185,8 @@ public class PMDTaskImpl { renderers.add(renderer); } try { - PMD.processFiles(configuration, rulesetList, files, renderers); + Report report = PMD.processFiles(configuration, rulesetList, files, renderers); + problemCount += report.getViolations().size(); } catch (ContextedRuntimeException e) { if (e.getFirstContextValue("filename") instanceof String) { handleError((String) e.getFirstContextValue("filename"), errorReport, e); @@ -201,7 +198,6 @@ public class PMDTaskImpl { } } - int problemCount = reportSize.get(); project.log(problemCount + " problems found", Project.MSG_VERBOSE); for (Formatter formatter : formatters) { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java index a77181b2e3..dd5b9081fb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java @@ -128,6 +128,10 @@ public abstract class AbstractPMDProcessor { public void processFiles(RuleSets rulesets, List files, RuleContext ctx, List renderers) { try { reportBrokenRules(ctx.getReport(), rulesets); + + // render base report first - general errors + renderReports(renderers, ctx.getReport()); + configuration.getAnalysisCache().checkValidity(rulesets, configuration.getClassLoader()); final SourceCodeProcessor processor = new SourceCodeProcessor(configuration); @@ -138,9 +142,6 @@ public abstract class AbstractPMDProcessor { runAnalysis(new PmdRunnable(dataSource, realFileName, renderers, ctx, rulesets, processor)); } - // render base report first - general errors - renderReports(renderers, ctx.getReport()); - // then add analysis results per file collectReports(renderers); } catch (RuntimeException e) { 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 0bdf85b5ad..cad90eac45 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 @@ -7,7 +7,6 @@ package net.sourceforge.pmd.ant; import static org.junit.Assert.fail; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -71,7 +70,7 @@ public class PMDTaskTest { } @Test - public void testWithShortFilenames() throws FileNotFoundException, IOException { + public void testWithShortFilenames() throws IOException { buildRule.executeTarget("testWithShortFilenames"); try (InputStream in = new FileInputStream("target/pmd-ant-test.txt")) { diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java index 5e7561cdf3..23d91a043d 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleDocGeneratorTest.java @@ -65,7 +65,7 @@ public class RuleDocGeneratorTest { RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.LOW, false, false, true); RuleSet ruleset = rsf.createRuleSet("rulesets/ruledoctest/sample.xml"); - generator.generate(Arrays.asList(ruleset).iterator(), + generator.generate(Arrays.asList(ruleset), Arrays.asList( "rulesets/ruledoctest/sample-deprecated.xml", "rulesets/ruledoctest/other-ruleset.xml")); From 0244ebf6e39a35ae9ede97feee0ad7acf7374109 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 25 Nov 2020 11:51:54 +0100 Subject: [PATCH 43/98] Fix javadoc --- .../net/sourceforge/pmd/RuleSetLoader.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) 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 53e65a8cba..6bde6e0dbb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -54,6 +54,7 @@ public final class RuleSetLoader { * Filter loaded rules to only those that match or are above * the given priority. The default is {@link RulePriority#LOW}, * ie, no filtering occurs. + * * @return This instance, modified */ public RuleSetLoader filterAbovePriority(RulePriority minimumPriority) { @@ -64,6 +65,7 @@ public final class RuleSetLoader { /** * Log a warning when referencing a deprecated rule. * This is enabled by default. + * * @return This instance, modified */ public RuleSetLoader warnDeprecated(boolean warn) { @@ -76,6 +78,7 @@ public final class RuleSetLoader { * been moved or renamed. This is enabled by default, if disabled, * unresolved references will not be translated and will produce an * error. + * * @return This instance, modified */ public RuleSetLoader enableCompatibility(boolean enable) { @@ -135,8 +138,8 @@ public final class RuleSetLoader { * @param paths Paths * * @throws RulesetLoadException If any error occurs (eg, invalid syntax, or resource not found), - * for any of the parameters - * @throws NullPointerException If the parameter, or any component is null + * for any of the parameters + * @throws NullPointerException If the parameter, or any component is null */ public List loadFromResources(Collection paths) { List ruleSets = new ArrayList<>(paths.size()); @@ -153,8 +156,8 @@ public final class RuleSetLoader { * @param rest Paths * * @throws RulesetLoadException If any error occurs (eg, invalid syntax, or resource not found), - * for any of the parameters - * @throws NullPointerException If the parameter, or any component is null + * for any of the parameters + * @throws NullPointerException If the parameter, or any component is null */ public List loadFromResources(String first, String... rest) { return loadFromResources(CollectionUtil.listOf(first, rest)); @@ -182,13 +185,15 @@ public final class RuleSetLoader { /** * Returns an Iterator of RuleSet objects loaded from descriptions from the - * "categories.properties" resource for each Language with Rule support. This + * "categories.properties" resource for each language. This * uses the classpath of the resource loader ({@link #loadResourcesWith(ClassLoader)}). * * @return A list of all category rulesets * - * @throws RuleSetNotFoundException if some ruleset file could not be parsed - * TODO shouldn't our API forbid this case? + * @throws RulesetLoadException If a standard ruleset cannot be loaded. + * This is a corner case, that probably should not be caught by clients. + * The standard rulesets are well-formed, at least in stock PMD distributions. + * */ public List getStandardRuleSets() { String rulesetsProperties; From f805b858504191c38c752578f8aac544f917f6fd Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Wed, 9 Dec 2020 20:11:25 -0800 Subject: [PATCH 44/98] Fix Html DataType This DataType does not need to be escaped, it is always escaped by the server. --- .../src/main/java/net/sourceforge/pmd/lang/vf/DataType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/DataType.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/DataType.java index 2910ea11d4..d172999181 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/DataType.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/DataType.java @@ -31,7 +31,7 @@ public enum DataType { ExternalLookup(true), File(false), Hierarchy(false), - Html(true), + Html(false), IndirectLookup(false), Location(false), LongTextArea(true), From 769cf5b316eac0668b28d1a7e99bbcc8c35429c9 Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Thu, 10 Dec 2020 13:21:48 +0100 Subject: [PATCH 45/98] Update C# grammar for additional C# 7 and C# 8 features --- .../pmd/lang/cs/antlr4/CSharpLexer.g4 | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/pmd-cs/src/main/antlr4/net/sourceforge/pmd/lang/cs/antlr4/CSharpLexer.g4 b/pmd-cs/src/main/antlr4/net/sourceforge/pmd/lang/cs/antlr4/CSharpLexer.g4 index f381392fa9..e1c213b93b 100644 --- a/pmd-cs/src/main/antlr4/net/sourceforge/pmd/lang/cs/antlr4/CSharpLexer.g4 +++ b/pmd-cs/src/main/antlr4/net/sourceforge/pmd/lang/cs/antlr4/CSharpLexer.g4 @@ -19,7 +19,8 @@ private boolean verbatium; BYTE_ORDER_MARK: '\u00EF\u00BB\u00BF'; SINGLE_LINE_DOC_COMMENT: '///' InputCharacter* -> channel(COMMENTS_CHANNEL); -DELIMITED_DOC_COMMENT: '/**' .*? '*/' -> channel(COMMENTS_CHANNEL); +EMPTY_DELIMITED_DOC_COMMENT: '/***/' -> channel(COMMENTS_CHANNEL); +DELIMITED_DOC_COMMENT: '/**' ~'/' .*? '*/' -> channel(COMMENTS_CHANNEL); SINGLE_LINE_COMMENT: '//' InputCharacter* -> channel(COMMENTS_CHANNEL); DELIMITED_COMMENT: '/*' .*? '*/' -> channel(COMMENTS_CHANNEL); @@ -119,6 +120,7 @@ TYPEOF: 'typeof'; UINT: 'uint'; ULONG: 'ulong'; UNCHECKED: 'unchecked'; +UNMANAGED: 'unmanaged'; UNSAFE: 'unsafe'; USHORT: 'ushort'; USING: 'using'; @@ -138,10 +140,11 @@ IDENTIFIER: '@'? IdentifierOrKeyword; //B.1.8 Literals // 0.Equals() would be parsed as an invalid real (1. branch) causing a lexer error -LITERAL_ACCESS: [0-9]+ IntegerTypeSuffix? '.' '@'? IdentifierOrKeyword; -INTEGER_LITERAL: [0-9]+ IntegerTypeSuffix?; -HEX_INTEGER_LITERAL: '0' [xX] HexDigit+ IntegerTypeSuffix?; -REAL_LITERAL: [0-9]* '.' [0-9]+ ExponentPart? [FfDdMm]? | [0-9]+ ([FfDdMm] | ExponentPart [FfDdMm]?); +LITERAL_ACCESS: [0-9] ('_'* [0-9])* IntegerTypeSuffix? '.' '@'? IdentifierOrKeyword; +INTEGER_LITERAL: [0-9] ('_'* [0-9])* IntegerTypeSuffix?; +HEX_INTEGER_LITERAL: '0' [xX] ('_'* HexDigit)+ IntegerTypeSuffix?; +BIN_INTEGER_LITERAL: '0' [bB] ('_'* [01])+ IntegerTypeSuffix?; +REAL_LITERAL: ([0-9] ('_'* [0-9])*)? '.' [0-9] ('_'* [0-9])* ExponentPart? [FfDdMm]? | [0-9] ('_'* [0-9])* ([FfDdMm] | ExponentPart [FfDdMm]?); CHARACTER_LITERAL: '\'' (~['\\\r\n\u0085\u2028\u2029] | CommonCharacter) '\''; REGULAR_STRING: '"' (~["\\\r\n\u0085\u2028\u2029] | CommonCharacter)* '"'; @@ -234,6 +237,8 @@ OP_OR_ASSIGNMENT: '|='; OP_XOR_ASSIGNMENT: '^='; OP_LEFT_SHIFT: '<<'; OP_LEFT_SHIFT_ASSIGNMENT: '<<='; +OP_COALESCING_ASSIGNMENT: '??='; +OP_RANGE: '..'; // https://msdn.microsoft.com/en-us/library/dn961160.aspx mode INTERPOLATION_STRING; @@ -271,6 +276,7 @@ WARNING: 'warning' Whitespace+ -> channel(DIREC REGION: 'region' Whitespace* -> channel(DIRECTIVE), mode(DIRECTIVE_TEXT); ENDREGION: 'endregion' Whitespace* -> channel(DIRECTIVE), mode(DIRECTIVE_TEXT); PRAGMA: 'pragma' Whitespace+ -> channel(DIRECTIVE), mode(DIRECTIVE_TEXT); +NULLABLE: 'nullable' Whitespace+ -> channel(DIRECTIVE), mode(DIRECTIVE_TEXT); DIRECTIVE_DEFAULT: 'default' -> channel(DIRECTIVE), type(DEFAULT); DIRECTIVE_HIDDEN: 'hidden' -> channel(DIRECTIVE); DIRECTIVE_OPEN_PARENS: '(' -> channel(DIRECTIVE), type(OPEN_PARENS); @@ -303,7 +309,7 @@ fragment NewLineCharacter ; fragment IntegerTypeSuffix: [lL]? [uU] | [uU]? [lL]; -fragment ExponentPart: [eE] ('+' | '-')? [0-9]+; +fragment ExponentPart: [eE] ('+' | '-')? [0-9] ('_'* [0-9])*; fragment CommonCharacter : SimpleEscapeSequence @@ -1102,4 +1108,4 @@ fragment UnicodeClassND | '\uaa50'..'\uaa59' | '\uabf0'..'\uabf9' | '\uff10'..'\uff19' - ; \ No newline at end of file + ; From 85c807ed1feeba9e6ffa8851ba951e307e434730 Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Thu, 10 Dec 2020 12:00:48 +0100 Subject: [PATCH 46/98] Add additional test cases for 2D arrays and more --- .../net/sourceforge/pmd/cpd/CsTokenizer.java | 42 +- .../pmd/lang/cs/cpd/testdata/listOfNumbers.cs | 18 +- .../lang/cs/cpd/testdata/listOfNumbers.txt | 693 ++++++++++-------- .../cs/cpd/testdata/listOfNumbers_ignored.txt | 25 + 4 files changed, 465 insertions(+), 313 deletions(-) diff --git a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java index 5ae96cbfa1..58cff112dc 100644 --- a/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java +++ b/pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java @@ -63,7 +63,7 @@ public class CsTokenizer extends AntlrTokenizer { private final boolean ignoreLiteralSequences; private boolean discardingUsings = false; private boolean discardingNL = false; - private boolean discardingLiterals = false; + private AntlrToken discardingLiteralsUntil = null; private boolean discardCurrent = false; CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences) { @@ -161,19 +161,24 @@ public class CsTokenizer extends AntlrTokenizer { private void skipLiteralSequences(final AntlrToken currentToken, final Iterable remainingTokens) { if (ignoreLiteralSequences) { final int type = currentToken.getKind(); - if (type == CSharpLexer.OPEN_BRACE && isSequenceOfLiterals(remainingTokens)) { - discardingLiterals = true; - } else if (type == CSharpLexer.CLOSE_BRACE && discardingLiterals) { - discardingLiterals = false; - discardCurrent = true; + if (isDiscardingLiterals()) { + if (currentToken == discardingLiteralsUntil) { // NOPMD - intentional check for reference equality + discardingLiteralsUntil = null; + discardCurrent = true; + } + } else if (type == CSharpLexer.OPEN_BRACE) { + final AntlrToken finalToken = findEndOfSequenceOfLiterals(remainingTokens); + discardingLiteralsUntil = finalToken; } } } - private boolean isSequenceOfLiterals(final Iterable remainingTokens) { + private AntlrToken findEndOfSequenceOfLiterals(final Iterable remainingTokens) { boolean seenLiteral = false; + int braceCount = 0; for (final AntlrToken token : remainingTokens) { switch (token.getKind()) { + case CSharpLexer.BIN_INTEGER_LITERAL: case CSharpLexer.CHARACTER_LITERAL: case CSharpLexer.HEX_INTEGER_LITERAL: case CSharpLexer.INTEGER_LITERAL: @@ -182,20 +187,33 @@ public class CsTokenizer extends AntlrTokenizer { break; // can be skipped; continue to the next token case CSharpLexer.COMMA: break; // can be skipped; continue to the next token + case CSharpLexer.OPEN_BRACE: + braceCount++; + break; // curly braces are allowed, as long as they're balanced case CSharpLexer.CLOSE_BRACE: - // end of the list; skip all contents - return seenLiteral; + braceCount--; + if (braceCount < 0) { + // end of the list; skip all contents + return seenLiteral ? token : null; + } else { + // curly braces are not yet balanced; continue to the next token + break; + } default: // some other token than the expected ones; this is not a sequence of literals - return false; + return null; } } - return false; + return null; + } + + public boolean isDiscardingLiterals() { + return discardingLiteralsUntil != null; } @Override protected boolean isLanguageSpecificDiscarding() { - return discardingUsings || discardingNL || discardingLiterals || discardCurrent; + return discardingUsings || discardingNL || isDiscardingLiterals() || discardCurrent; } } } diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs index a3ceee1dca..6ebda7d3ff 100644 --- a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs @@ -3,6 +3,22 @@ using System.Collections; using System.Collections.Generic; public class LongLists { List l = new List { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + }; + byte[,] a = {1,2,3,4,5}; + byte[,] b = {{1,2},{3,4},{5,6}}; + int[,] c = { + 157, // decimal literal + 0377, // octal literal + 36_000_000, // literal with digit separators + 0x3fff, // hexadecimal literal + 0X3FFF, // same hexadecimal literal + 328u, // unsigned value + 0x7FFFFFL, // long value + 0776745ul, // unsigned long value + 18.46, // float + 18.46e0, // double with exponent + 18.46e1, // double with exponent + 0b000001, // binary literal }; } diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt index 17d20f914d..765029a808 100644 --- a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt @@ -36,309 +36,402 @@ L5 [>] 33 33 [{] 35 35 L6 - [0] 6 6 - [,] 7 7 - [0] 8 8 - [,] 9 9 - [0] 10 10 - [,] 11 11 - [0] 12 12 - [,] 13 13 - [0] 14 14 - [,] 15 15 - [0] 16 16 - [,] 17 17 - [0] 18 18 - [,] 19 19 - [0] 20 20 - [,] 21 21 - [0] 22 22 - [,] 23 23 - [0] 24 24 - [,] 25 25 - [0] 26 26 - [,] 27 27 - [0] 28 28 - [,] 29 29 - [0] 30 30 - [,] 31 31 - [0] 32 32 - [,] 33 33 - [0] 34 34 - [,] 35 35 - [0] 36 36 - [,] 37 37 - [0] 38 38 - [,] 39 39 - [0] 40 40 - [,] 41 41 - [0] 42 42 - [,] 43 43 - [0] 44 44 - [,] 45 45 - [0] 46 46 - [,] 47 47 - [0] 48 48 - [,] 49 49 - [0] 50 50 - [,] 51 51 - [0] 52 52 - [,] 53 53 - [0] 54 54 - [,] 55 55 - [0] 56 56 - [,] 57 57 - [0] 58 58 - [,] 59 59 - [0] 60 60 - [,] 61 61 - [0] 62 62 - [,] 63 63 - [0] 64 64 - [,] 65 65 - [0] 66 66 - [,] 67 67 - [0] 68 68 - [,] 69 69 - [0] 70 70 - [,] 71 71 - [0] 72 72 - [,] 73 73 - [0] 74 74 - [,] 75 75 - [0] 76 76 - [,] 77 77 - [0] 78 78 - [,] 79 79 - [0] 80 80 - [,] 81 81 - [0] 82 82 - [,] 83 83 - [0] 84 84 - [,] 85 85 - [0] 86 86 - [,] 87 87 - [0] 88 88 - [,] 89 89 - [0] 90 90 - [,] 91 91 - [0] 92 92 - [,] 93 93 - [0] 94 94 - [,] 95 95 - [0] 96 96 - [,] 97 97 - [0] 98 98 - [,] 99 99 - [0] 100 100 - [,] 101 101 - [0] 102 102 - [,] 103 103 - [0] 104 104 - [,] 105 105 - [0] 106 106 - [,] 107 107 - [0] 108 108 - [,] 109 109 - [0] 110 110 - [,] 111 111 - [0] 112 112 - [,] 113 113 - [0] 114 114 - [,] 115 115 - [0] 116 116 - [,] 117 117 - [0] 118 118 - [,] 119 119 - [0] 120 120 - [,] 121 121 - [0] 122 122 - [,] 123 123 - [0] 124 124 - [,] 125 125 - [0] 126 126 - [,] 127 127 - [0] 128 128 - [,] 129 129 - [0] 130 130 - [,] 131 131 - [0] 132 132 - [,] 133 133 - [0] 134 134 - [,] 135 135 - [0] 136 136 - [,] 137 137 - [0] 138 138 - [,] 139 139 - [0] 140 140 - [,] 141 141 - [0] 142 142 - [,] 143 143 - [0] 144 144 - [,] 145 145 - [0] 146 146 - [,] 147 147 - [0] 148 148 - [,] 149 149 - [0] 150 150 - [,] 151 151 - [0] 152 152 - [,] 153 153 - [0] 154 154 - [,] 155 155 - [0] 156 156 - [,] 157 157 - [0] 158 158 - [,] 159 159 - [0] 160 160 - [,] 161 161 - [0] 162 162 - [,] 163 163 - [0] 164 164 - [,] 165 165 - [0] 166 166 - [,] 167 167 - [0] 168 168 - [,] 169 169 - [0] 170 170 - [,] 171 171 - [0] 172 172 - [,] 173 173 - [0] 174 174 - [,] 175 175 - [0] 176 176 - [,] 177 177 - [0] 178 178 - [,] 179 179 - [0] 180 180 - [,] 181 181 - [0] 182 182 - [,] 183 183 - [0] 184 184 - [,] 185 185 - [0] 186 186 - [,] 187 187 - [0] 188 188 - [,] 189 189 - [0] 190 190 - [,] 191 191 - [0] 192 192 - [,] 193 193 - [0] 194 194 - [,] 195 195 - [0] 196 196 - [,] 197 197 - [0] 198 198 - [,] 199 199 - [0] 200 200 - [,] 201 201 - [0] 202 202 - [,] 203 203 - [0] 204 204 - [,] 205 205 - [0] 206 206 - [,] 207 207 - [0] 208 208 - [,] 209 209 - [0] 210 210 - [,] 211 211 - [0] 212 212 - [,] 213 213 - [0] 214 214 - [,] 215 215 - [0] 216 216 - [,] 217 217 - [0] 218 218 - [,] 219 219 - [0] 220 220 - [,] 221 221 - [0] 222 222 - [,] 223 223 - [0] 224 224 - [,] 225 225 - [0] 226 226 - [,] 227 227 - [0] 228 228 - [,] 229 229 - [0] 230 230 - [,] 231 231 - [0] 232 232 - [,] 233 233 - [0] 234 234 - [,] 235 235 - [0] 236 236 - [,] 237 237 - [0] 238 238 - [,] 239 239 - [0] 240 240 - [,] 241 241 - [0] 242 242 - [,] 243 243 - [0] 244 244 - [,] 245 245 - [0] 246 246 - [,] 247 247 - [0] 248 248 - [,] 249 249 - [0] 250 250 - [,] 251 251 - [0] 252 252 - [,] 253 253 - [0] 254 254 - [,] 255 255 - [0] 256 256 - [,] 257 257 - [0] 258 258 - [,] 259 259 - [0] 260 260 - [,] 261 261 - [0] 262 262 - [,] 263 263 - [0] 264 264 - [,] 265 265 - [0] 266 266 - [,] 267 267 - [0] 268 268 - [,] 269 269 - [0] 270 270 - [,] 271 271 - [0] 272 272 - [,] 273 273 - [0] 274 274 - [,] 275 275 - [0] 276 276 - [,] 277 277 - [0] 278 278 - [,] 279 279 - [0] 280 280 - [,] 281 281 - [0] 282 282 - [,] 283 283 - [0] 284 284 - [,] 285 285 - [0] 286 286 - [,] 287 287 - [0] 288 288 - [,] 289 289 - [0] 290 290 - [,] 291 291 - [0] 292 292 - [,] 293 293 - [0] 294 294 - [,] 295 295 - [0] 296 296 - [,] 297 297 - [0] 298 298 - [,] 299 299 - [0] 300 300 - [,] 301 301 - [0] 302 302 - [,] 303 303 - [0] 304 304 - [,] 305 305 + [0] 7 7 + [,] 8 8 + [0] 9 9 + [,] 10 10 + [0] 11 11 + [,] 12 12 + [0] 13 13 + [,] 14 14 + [0] 15 15 + [,] 16 16 + [0] 17 17 + [,] 18 18 + [0] 19 19 + [,] 20 20 + [0] 21 21 + [,] 22 22 + [0] 23 23 + [,] 24 24 + [0] 25 25 + [,] 26 26 + [0] 27 27 + [,] 28 28 + [0] 29 29 + [,] 30 30 + [0] 31 31 + [,] 32 32 + [0] 33 33 + [,] 34 34 + [0] 35 35 + [,] 36 36 + [0] 37 37 + [,] 38 38 + [0] 39 39 + [,] 40 40 + [0] 41 41 + [,] 42 42 + [0] 43 43 + [,] 44 44 + [0] 45 45 + [,] 46 46 + [0] 47 47 + [,] 48 48 + [0] 49 49 + [,] 50 50 + [0] 51 51 + [,] 52 52 + [0] 53 53 + [,] 54 54 + [0] 55 55 + [,] 56 56 + [0] 57 57 + [,] 58 58 + [0] 59 59 + [,] 60 60 + [0] 61 61 + [,] 62 62 + [0] 63 63 + [,] 64 64 + [0] 65 65 + [,] 66 66 + [0] 67 67 + [,] 68 68 + [0] 69 69 + [,] 70 70 + [0] 71 71 + [,] 72 72 + [0] 73 73 + [,] 74 74 + [0] 75 75 + [,] 76 76 + [0] 77 77 + [,] 78 78 + [0] 79 79 + [,] 80 80 + [0] 81 81 + [,] 82 82 + [0] 83 83 + [,] 84 84 + [0] 85 85 + [,] 86 86 + [0] 87 87 + [,] 88 88 + [0] 89 89 + [,] 90 90 + [0] 91 91 + [,] 92 92 + [0] 93 93 + [,] 94 94 + [0] 95 95 + [,] 96 96 + [0] 97 97 + [,] 98 98 + [0] 99 99 + [,] 100 100 + [0] 101 101 + [,] 102 102 + [0] 103 103 + [,] 104 104 + [0] 105 105 + [,] 106 106 + [0] 107 107 + [,] 108 108 + [0] 109 109 + [,] 110 110 + [0] 111 111 + [,] 112 112 + [0] 113 113 + [,] 114 114 + [0] 115 115 + [,] 116 116 + [0] 117 117 + [,] 118 118 + [0] 119 119 + [,] 120 120 + [0] 121 121 + [,] 122 122 + [0] 123 123 + [,] 124 124 + [0] 125 125 + [,] 126 126 + [0] 127 127 + [,] 128 128 + [0] 129 129 + [,] 130 130 + [0] 131 131 + [,] 132 132 + [0] 133 133 + [,] 134 134 + [0] 135 135 + [,] 136 136 + [0] 137 137 + [,] 138 138 + [0] 139 139 + [,] 140 140 + [0] 141 141 + [,] 142 142 + [0] 143 143 + [,] 144 144 + [0] 145 145 + [,] 146 146 + [0] 147 147 + [,] 148 148 + [0] 149 149 + [,] 150 150 + [0] 151 151 + [,] 152 152 + [0] 153 153 + [,] 154 154 + [0] 155 155 + [,] 156 156 + [0] 157 157 + [,] 158 158 + [0] 159 159 + [,] 160 160 + [0] 161 161 + [,] 162 162 + [0] 163 163 + [,] 164 164 + [0] 165 165 + [,] 166 166 + [0] 167 167 + [,] 168 168 + [0] 169 169 + [,] 170 170 + [0] 171 171 + [,] 172 172 + [0] 173 173 + [,] 174 174 + [0] 175 175 + [,] 176 176 + [0] 177 177 + [,] 178 178 + [0] 179 179 + [,] 180 180 + [0] 181 181 + [,] 182 182 + [0] 183 183 + [,] 184 184 + [0] 185 185 + [,] 186 186 + [0] 187 187 + [,] 188 188 + [0] 189 189 + [,] 190 190 + [0] 191 191 + [,] 192 192 + [0] 193 193 + [,] 194 194 + [0] 195 195 + [,] 196 196 + [0] 197 197 + [,] 198 198 + [0] 199 199 + [,] 200 200 + [0] 201 201 + [,] 202 202 + [0] 203 203 + [,] 204 204 + [0] 205 205 + [,] 206 206 + [0] 207 207 + [,] 208 208 + [0] 209 209 + [,] 210 210 + [0] 211 211 + [,] 212 212 + [0] 213 213 + [,] 214 214 + [0] 215 215 + [,] 216 216 + [0] 217 217 + [,] 218 218 + [0] 219 219 + [,] 220 220 + [0] 221 221 + [,] 222 222 + [0] 223 223 + [,] 224 224 + [0] 225 225 + [,] 226 226 + [0] 227 227 + [,] 228 228 + [0] 229 229 + [,] 230 230 + [0] 231 231 + [,] 232 232 + [0] 233 233 + [,] 234 234 + [0] 235 235 + [,] 236 236 + [0] 237 237 + [,] 238 238 + [0] 239 239 + [,] 240 240 + [0] 241 241 + [,] 242 242 + [0] 243 243 + [,] 244 244 + [0] 245 245 + [,] 246 246 + [0] 247 247 + [,] 248 248 + [0] 249 249 + [,] 250 250 + [0] 251 251 + [,] 252 252 + [0] 253 253 + [,] 254 254 + [0] 255 255 + [,] 256 256 + [0] 257 257 + [,] 258 258 + [0] 259 259 + [,] 260 260 + [0] 261 261 + [,] 262 262 + [0] 263 263 + [,] 264 264 + [0] 265 265 + [,] 266 266 + [0] 267 267 + [,] 268 268 + [0] 269 269 + [,] 270 270 + [0] 271 271 + [,] 272 272 + [0] 273 273 + [,] 274 274 + [0] 275 275 + [,] 276 276 + [0] 277 277 + [,] 278 278 + [0] 279 279 + [,] 280 280 + [0] 281 281 + [,] 282 282 + [0] 283 283 + [,] 284 284 + [0] 285 285 + [,] 286 286 + [0] 287 287 + [,] 288 288 + [0] 289 289 + [,] 290 290 + [0] 291 291 + [,] 292 292 + [0] 293 293 + [,] 294 294 + [0] 295 295 + [,] 296 296 + [0] 297 297 + [,] 298 298 + [0] 299 299 + [,] 300 300 + [0] 301 301 + [,] 302 302 + [0] 303 303 + [,] 304 304 + [0] 305 305 + [,] 306 306 L7 [}] 5 5 [;] 6 6 L8 + [byte] 5 8 + [\[] 9 9 + [,] 10 10 + [\]] 11 11 + [a] 13 13 + [=] 15 15 + [{] 17 17 + [1] 18 18 + [,] 19 19 + [2] 20 20 + [,] 21 21 + [3] 22 22 + [,] 23 23 + [4] 24 24 + [,] 25 25 + [5] 26 26 + [}] 27 27 + [;] 28 28 +L9 + [byte] 5 8 + [\[] 9 9 + [,] 10 10 + [\]] 11 11 + [b] 13 13 + [=] 15 15 + [{] 17 17 + [{] 18 18 + [1] 19 19 + [,] 20 20 + [2] 21 21 + [}] 22 22 + [,] 23 23 + [{] 24 24 + [3] 25 25 + [,] 26 26 + [4] 27 27 + [}] 28 28 + [,] 29 29 + [{] 30 30 + [5] 31 31 + [,] 32 32 + [6] 33 33 + [}] 34 34 + [}] 35 35 + [;] 36 36 +L10 + [int] 5 7 + [\[] 8 8 + [,] 9 9 + [\]] 10 10 + [c] 12 12 + [=] 14 14 + [{] 16 16 +L11 + [157] 7 9 + [,] 10 10 +L12 + [0377] 7 10 + [,] 11 11 +L13 + [36_000_000] 7 16 + [,] 17 17 +L14 + [0x3fff] 7 12 + [,] 13 13 +L15 + [0X3FFF] 7 12 + [,] 13 13 +L16 + [328u] 7 10 + [,] 11 11 +L17 + [0x7FFFFFL] 7 15 + [,] 16 16 +L18 + [0776745ul] 7 15 + [,] 16 16 +L19 + [18.46] 7 11 + [,] 12 12 +L20 + [18.46e0] 7 13 + [,] 14 14 +L21 + [18.46e1] 7 13 + [,] 14 14 +L22 + [0b000001] 7 14 + [,] 15 15 +L23 + [}] 5 5 + [;] 6 6 +L24 [}] 1 1 EOF diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt index 03f835fdca..79b782b67d 100644 --- a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt @@ -37,5 +37,30 @@ L5 L7 [;] 6 6 L8 + [byte] 5 8 + [\[] 9 9 + [,] 10 10 + [\]] 11 11 + [a] 13 13 + [=] 15 15 + [;] 28 28 +L9 + [byte] 5 8 + [\[] 9 9 + [,] 10 10 + [\]] 11 11 + [b] 13 13 + [=] 15 15 + [;] 36 36 +L10 + [int] 5 7 + [\[] 8 8 + [,] 9 9 + [\]] 10 10 + [c] 12 12 + [=] 14 14 +L23 + [;] 6 6 +L24 [}] 1 1 EOF From f7829531c966c2e3bff97e08e0cc0c49ab500976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 10 Dec 2020 20:15:18 +0100 Subject: [PATCH 47/98] Add some doc --- .../java/net/sourceforge/pmd/lang/ParserOptions.java | 11 +++++++---- .../sourceforge/pmd/lang/ParserOptionsUnitTest.java | 4 ++-- .../net/sourceforge/pmd/lang/vf/VfParserOptions.java | 4 ++++ .../sourceforge/pmd/lang/vf/VfParserOptionsTest.java | 6 ++++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java index a5e0e88b3f..b4702c32db 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java @@ -31,11 +31,12 @@ public class ParserOptions { private final ParserOptionsProperties parserOptionsProperties; public ParserOptions() { - this(null); + this.languageId = null; + this.parserOptionsProperties = new ParserOptionsProperties(); } public ParserOptions(String languageId) { - this.languageId = languageId; + this.languageId = Objects.requireNonNull(languageId); this.parserOptionsProperties = new ParserOptionsProperties(); } @@ -98,7 +99,9 @@ public class ParserOptions { * @return environment variable that overrides the PropertyDesciptors default value. Returns null if no environment * variable has been set. */ - protected String getEnvValue(PropertyDescriptor propertyDescriptor) { + String getEnvValue(PropertyDescriptor propertyDescriptor) { + // note: since we use environent variables and not system properties, + // tests override this method. return System.getenv(getEnvironmentVariableName(propertyDescriptor)); } @@ -106,7 +109,7 @@ public class ParserOptions { * Overrides the default PropertyDescriptors with values found in environment variables. * TODO: Move this to net.sourceforge.pmd.PMD#parserFor when CLI options are implemented */ - protected void overridePropertiesFromEnv() { + protected final void overridePropertiesFromEnv() { for (PropertyDescriptor propertyDescriptor : parserOptionsProperties.getPropertyDescriptors()) { String propertyValue = getEnvValue(propertyDescriptor); diff --git a/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java b/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java index 239a22df89..0fc7a8aa10 100644 --- a/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java +++ b/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java @@ -86,7 +86,7 @@ public class ParserOptionsUnitTest { public void testEnvOverriddenPropertyDescriptors() { TestParserOptions parserOptions = new TestParserOptions() { @Override - protected String getEnvValue(PropertyDescriptor propertyDescriptor) { + protected String getEnvValue(PropertyDescriptor propertyDescriptor) { if (propertyDescriptor.equals(TestParserOptions.LIST_DESCRIPTOR)) { return StringUtils.join(OVERRIDDEN_LIST, ","); } else if (propertyDescriptor.equals(TestParserOptions.STRING_DESCRIPTOR)) { @@ -105,7 +105,7 @@ public class ParserOptionsUnitTest { public void testEmptyPropertyDescriptors() { TestParserOptions vfParserOptions = new TestParserOptions() { @Override - protected String getEnvValue(PropertyDescriptor propertyDescriptor) { + protected String getEnvValue(PropertyDescriptor propertyDescriptor) { if (propertyDescriptor.equals(TestParserOptions.LIST_DESCRIPTOR) || propertyDescriptor.equals(TestParserOptions.STRING_DESCRIPTOR)) { return ""; diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java index c69be0a95b..af73dc35d2 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java @@ -18,6 +18,8 @@ public class VfParserOptions extends ParserOptions { /** * Directory that contains Apex classes that may be referenced from a Visualforce page. + * + *

Env variable is {@code PMD_VF_APEXDIRECTORIES}. */ public static final PropertyDescriptor> APEX_DIRECTORIES_DESCRIPTOR = PropertyFactory.stringListProperty("apexDirectories") @@ -28,6 +30,8 @@ public class VfParserOptions extends ParserOptions { /** * Directory that contains Object definitions that may be referenced from a Visualforce page. + * + *

Env variable is {@code PMD_VF_OBJECTSDIRECTORIES}. */ public static final PropertyDescriptor> OBJECTS_DIRECTORIES_DESCRIPTOR = PropertyFactory.stringListProperty("objectsDirectories") diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java index dc124f19fd..5c238b1157 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java @@ -9,12 +9,14 @@ import static org.junit.Assert.assertEquals; import org.junit.Test; public class VfParserOptionsTest { + @Test public void testDefaultPropertyDescriptors() { VfParserOptions vfParserOptions = new VfParserOptions(); assertEquals(VfParserOptions.DEFAULT_APEX_DIRECTORIES, - vfParserOptions.getProperty(VfParserOptions.APEX_DIRECTORIES_DESCRIPTOR)); + vfParserOptions.getProperty(VfParserOptions.APEX_DIRECTORIES_DESCRIPTOR)); assertEquals(VfParserOptions.DEFAULT_OBJECT_DIRECTORIES, - vfParserOptions.getProperty(VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR)); + vfParserOptions.getProperty(VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR)); } + } From aaa540af7b7e742c47db8e02b68564e3fd931e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 10 Dec 2020 20:26:58 +0100 Subject: [PATCH 48/98] Update release notes --- docs/pages/release_notes.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index cf766b7686..8627978dab 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -20,6 +20,19 @@ This is a {{ site.pmd.release_type }} release. * The Scala module now supports [suppression](https://pmd.github.io/latest/pmd_userdocs_cpd.html#suppression) through `CPD-ON`/`CPD-OFF` comment pairs. See [#2929](https://github.com/pmd/pmd/pull/2929) + +##### Type information for VisualForce + +The VisualForce AST now can resolve the data type of Apex expressions mentioned in `` elements. This feature improves the precision of existing rules, like {% rule vf/security/VfUnescapeEl %}. + +This can be configured using two environment variables: +* `PMD_VF_APEXDIRECTORIES`: Comma separated list of directories for Apex classes. Absolute or relative to the Visualforce directory. Default is `../classes` +* `PMD_VF_OBJECTSDIRECTORIES`: Comma separated list of directories for CustomObjects. Absolute or relative to the Visualforce directory. Default is `../objects` + +This feature is experimental, in particular, expect changes to the way the configuration is specified. We'll probably extend the CLI instead of relying on environment variables in a future version. + +Thanks to Jeff Bartolotta for contributing this! + ### Fixed Issues * core @@ -64,6 +77,7 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr ### External Contributions +* [#2864](https://github.com/pmd/pmd/pull/2864): [vf] Provide expression type information to Visualforce rules to avoid false positives - [Jeff Bartolotta](https://github.com/jbartolotta-sfdc) * [#2914](https://github.com/pmd/pmd/pull/2914): \[core] Include rule name in text renderer - [Gunther Schrijvers](https://github.com/GuntherSchrijvers) * [#2925](https://github.com/pmd/pmd/pull/2925): Cleanup: Correct annotation array initializer indents from checkstyle #8083 - [Abhishek Kumar](https://github.com/Abhishek-kumar09) * [#2929](https://github.com/pmd/pmd/pull/2929): \[scala] Add support for CPD-ON and CPD-OFF special comments - [Andy Robinson](https://github.com/andyrobinson) From 4e00c204991b8a3bbff9012b1d6f264e14f8166e Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Thu, 10 Dec 2020 22:20:10 -0800 Subject: [PATCH 49/98] Small comment and doc edits --- docs/pages/release_notes.md | 8 ++++---- .../java/net/sourceforge/pmd/lang/vf/VfParserOptions.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 8627978dab..68e200ae2c 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -23,15 +23,15 @@ This is a {{ site.pmd.release_type }} release. ##### Type information for VisualForce -The VisualForce AST now can resolve the data type of Apex expressions mentioned in `` elements. This feature improves the precision of existing rules, like {% rule vf/security/VfUnescapeEl %}. +The Visualforce AST now can resolve the data type of Visualforce expressions that reference Apex Controller properties and Custom Object fields. This feature improves the precision of existing rules, like {% rule vf/security/VfUnescapeEl %}. This can be configured using two environment variables: -* `PMD_VF_APEXDIRECTORIES`: Comma separated list of directories for Apex classes. Absolute or relative to the Visualforce directory. Default is `../classes` -* `PMD_VF_OBJECTSDIRECTORIES`: Comma separated list of directories for CustomObjects. Absolute or relative to the Visualforce directory. Default is `../objects` +* `PMD_VF_APEXDIRECTORIES`: Comma separated list of directories for Apex classes. Absolute or relative to the Visualforce directory. Default is `../classes`. Specifying an empty string will disable data type resolution for Apex Controller properties. +* `PMD_VF_OBJECTSDIRECTORIES`: Comma separated list of directories for Custom Objects. Absolute or relative to the Visualforce directory. Default is `../objects`. Specifying an empty string will disable data type resolution for Custom Object fields. This feature is experimental, in particular, expect changes to the way the configuration is specified. We'll probably extend the CLI instead of relying on environment variables in a future version. -Thanks to Jeff Bartolotta for contributing this! +Thanks to Jeff Bartolotta and Roopa Mohan for contributing this! ### Fixed Issues diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java index af73dc35d2..16ba794e88 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java @@ -35,7 +35,7 @@ public class VfParserOptions extends ParserOptions { */ public static final PropertyDescriptor> OBJECTS_DIRECTORIES_DESCRIPTOR = PropertyFactory.stringListProperty("objectsDirectories") - .desc("Location of CustomObject directories. Absolute or relative to the Visualforce directory.") + .desc("Location of Custom Object directories. Absolute or relative to the Visualforce directory.") .defaultValue(DEFAULT_OBJECT_DIRECTORIES) .delim(',') .build(); From da3943bf171d669223111d60dcf36f16256c0026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 11 Dec 2020 11:34:50 +0100 Subject: [PATCH 50/98] Update release notes, refs #2962 --- docs/pages/release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 68e200ae2c..d46528bf77 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -84,5 +84,6 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr * [#2936](https://github.com/pmd/pmd/pull/2936): \[java] (doc) Fix typo: "an accessor" not "a" - [Igor Moreno](https://github.com/igormoreno) * [#2938](https://github.com/pmd/pmd/pull/2938): \[cs] CPD: fix issue where ignoring using directives could not be disabled - [Maikel Steneker](https://github.com/maikelsteneker) * [#2945](https://github.com/pmd/pmd/pull/2945): \[cs] Add option to ignore sequences of literals - [Maikel Steneker](https://github.com/maikelsteneker) +* [#2962](https://github.com/pmd/pmd/pull/2962): \[cpp] Add support for C++ 14 binary literals - [Maikel Steneker](https://github.com/maikelsteneker) {% endtocmaker %} From 85ed4f3303b0ded7a5a2068d8b850052e0ab9288 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 11 Dec 2020 16:12:00 +0100 Subject: [PATCH 51/98] [ci] Fix build badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d07be46f1f..ca8a2941b8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # PMD [![Join the chat at https://gitter.im/pmd/pmd](https://badges.gitter.im/pmd/pmd.svg)](https://gitter.im/pmd/pmd?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -[![Build Status](https://github.com/pmd/pmd/workflows/.github/workflows/pushes.yml/badge.svg?branch=master)](https://github.com/pmd/pmd/actions) +[![Build Status](https://github.com/pmd/pmd/workflows/Pushes/badge.svg?branch=master)](https://github.com/pmd/pmd/actions) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/net.sourceforge.pmd/pmd/badge.svg)](https://maven-badges.herokuapp.com/maven-central/net.sourceforge.pmd/pmd) [![Reproducible Builds](https://img.shields.io/badge/Reproducible_Builds-ok-green?labelColor=blue)](https://github.com/jvm-repo-rebuild/reproducible-central#net.sourceforge.pmd:pmd) [![Coverage Status](https://coveralls.io/repos/github/pmd/pmd/badge.svg)](https://coveralls.io/github/pmd/pmd) From feccab274e4209eea1ee661042682dff29ed65ec Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 11 Dec 2020 16:50:49 +0100 Subject: [PATCH 52/98] Bump org.codehaus.groovy:groovy from 2.4.7 to 2.4.21 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b47efa1f31..ba793f9856 100644 --- a/pom.xml +++ b/pom.xml @@ -724,7 +724,7 @@ org.codehaus.groovy groovy - 2.4.7 + 2.4.21 From 422934317314efdb93adc708c1fb9b6c171ee01e Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 11 Dec 2020 16:52:17 +0100 Subject: [PATCH 53/98] Use Github Discussions for Q&A --- .github/ISSUE_TEMPLATE/config.yml | 3 +++ .github/ISSUE_TEMPLATE/question.md | 14 -------------- 2 files changed, 3 insertions(+), 14 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/question.md diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 56a5a13ccb..738f691819 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,8 @@ blank_issues_enabled: false contact_links: + - name: Question + url: https://github.com/pmd/pmd/discussions?discussions_q=category%3AQ%26A + about: Feel free to ask any question about PMD and its usage - name: PMD Designer Issues url: https://github.com/pmd/pmd-designer/issues about: Issues about the rule designer diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index 5af12acfa1..0000000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: Question -about: Feel free to ask any question about PMD and its usage -title: '' -labels: 'a:question' -assignees: '' - ---- - - - -**Description:** - From bcbf588bba76c16e5d26121b16d4d7d0a85c174e Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 11 Dec 2020 18:35:41 +0100 Subject: [PATCH 54/98] Rename RuleSetLoadException --- ...setLoadException.java => RuleSetLoadException.java} | 6 +++--- .../main/java/net/sourceforge/pmd/RuleSetLoader.java | 10 +++++----- .../net/sourceforge/pmd/RuleSetNotFoundException.java | 2 +- .../net/sourceforge/pmd/docs/RuleDocGenerator.java | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) rename pmd-core/src/main/java/net/sourceforge/pmd/{RulesetLoadException.java => RuleSetLoadException.java} (80%) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetLoadException.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java similarity index 80% rename from pmd-core/src/main/java/net/sourceforge/pmd/RulesetLoadException.java rename to pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java index 7979971118..a59321e146 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetLoadException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java @@ -15,17 +15,17 @@ import net.sourceforge.pmd.annotation.InternalApi; *

In the new {@link RuleSetLoader} API, this is thrown instead of * {@link RuleSetNotFoundException}. */ -public final class RulesetLoadException extends RuntimeException { +public final class RuleSetLoadException extends RuntimeException { /** Constructors are internal. */ @InternalApi - public RulesetLoadException(String message, Throwable cause) { + public RuleSetLoadException(String message, Throwable cause) { super(message, cause); } /** Constructors are internal. */ @InternalApi - public RulesetLoadException(String message) { + public RuleSetLoadException(String message) { super(message); } 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 6bde6e0dbb..61f441db7e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -126,7 +126,7 @@ public final class RuleSetLoader { * * @param rulesetPath A reference to a single ruleset * - * @throws RulesetLoadException If any error occurs (eg, invalid syntax, or resource not found) + * @throws RuleSetLoadException If any error occurs (eg, invalid syntax, or resource not found) */ public RuleSet loadFromResource(String rulesetPath) { return loadFromResource(new RuleSetReferenceId(rulesetPath)); @@ -137,7 +137,7 @@ public final class RuleSetLoader { * * @param paths Paths * - * @throws RulesetLoadException If any error occurs (eg, invalid syntax, or resource not found), + * @throws RuleSetLoadException If any error occurs (eg, invalid syntax, or resource not found), * for any of the parameters * @throws NullPointerException If the parameter, or any component is null */ @@ -155,7 +155,7 @@ public final class RuleSetLoader { * @param first First path * @param rest Paths * - * @throws RulesetLoadException If any error occurs (eg, invalid syntax, or resource not found), + * @throws RuleSetLoadException If any error occurs (eg, invalid syntax, or resource not found), * for any of the parameters * @throws NullPointerException If the parameter, or any component is null */ @@ -168,7 +168,7 @@ public final class RuleSetLoader { try { return toFactory().createRuleSet(ruleSetReferenceId); } catch (Exception e) { - throw new RulesetLoadException("Cannot parse " + ruleSetReferenceId, e); + throw new RuleSetLoadException("Cannot parse " + ruleSetReferenceId, e); } } @@ -190,7 +190,7 @@ public final class RuleSetLoader { * * @return A list of all category rulesets * - * @throws RulesetLoadException If a standard ruleset cannot be loaded. + * @throws RuleSetLoadException If a standard ruleset cannot be loaded. * This is a corner case, that probably should not be caught by clients. * The standard rulesets are well-formed, at least in stock PMD distributions. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java index 05dd3d4be2..4e3acad5d4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java @@ -6,7 +6,7 @@ package net.sourceforge.pmd; /** * @deprecated This is now only thrown by deprecated apis. {@link RuleSetLoader} - * throws {@link RulesetLoadException} instead + * throws {@link RuleSetLoadException} instead */ @Deprecated public class RuleSetNotFoundException extends Exception { diff --git a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java index 2b5d83c0cc..6f7c7e60ce 100644 --- a/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java +++ b/pmd-doc/src/main/java/net/sourceforge/pmd/docs/RuleDocGenerator.java @@ -36,8 +36,8 @@ import org.apache.commons.text.StringEscapeUtils; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; +import net.sourceforge.pmd.RuleSetLoadException; import net.sourceforge.pmd.RuleSetLoader; -import net.sourceforge.pmd.RulesetLoadException; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.lang.rule.XPathRule; @@ -122,7 +122,7 @@ public class RuleDocGenerator { } else { LOG.fine("Ignoring ruleset " + filename); } - } catch (RulesetLoadException e) { + } catch (RuleSetLoadException e) { // ignore rulesets, we can't read LOG.log(Level.WARNING, "ruleset file " + filename + " ignored (" + e.getMessage() + ")", e); } From afcb45ea89fec1820fb0513962149cb43beece9f Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 11 Dec 2020 18:47:19 +0100 Subject: [PATCH 55/98] Remove unneeded suppresswarnings in AbstractPMDProcessor --- .../net/sourceforge/pmd/processor/AbstractPMDProcessor.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java index dd5b9081fb..5bcf538f21 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java @@ -112,9 +112,6 @@ public abstract class AbstractPMDProcessor { return brokenRules; } - @SuppressWarnings("PMD.CloseResource") - // the data sources must only be closed after the threads are finished - // this is done manually without a try-with-resources @Deprecated public void processFiles(RuleSetFactory ruleSetFactory, List files, RuleContext ctx, List renderers) { From 39f3026cf141b1f954e529e1a090f8abb5408bc4 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 12 Dec 2020 09:31:08 +0100 Subject: [PATCH 56/98] Prepare pmd release 6.30.0 --- docs/_config.yml | 4 +-- docs/pages/next_major_development.md | 41 ++++++++++++++++++++++++++++ docs/pages/release_notes.md | 6 ++++ 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/docs/_config.yml b/docs/_config.yml index ddc9c229f1..d28e322762 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,9 +1,9 @@ repository: pmd/pmd pmd: - version: 6.30.0-SNAPSHOT + version: 6.30.0 previous_version: 6.29.0 - date: ??-November-2020 + date: 12-December-2020 release_type: minor # release types: major, minor, bugfix diff --git a/docs/pages/next_major_development.md b/docs/pages/next_major_development.md index 323c9757b9..d8b0d6a550 100644 --- a/docs/pages/next_major_development.md +++ b/docs/pages/next_major_development.md @@ -125,6 +125,47 @@ the breaking API changes will be performed in 7.0.0. an API is tagged as `@Deprecated` or not in the latest minor release. During the development of 7.0.0, we may decide to remove some APIs that were not tagged as deprecated, though we'll try to avoid it." %} +#### 6.30.0 + +##### Deprecated API + +###### Around RuleSet parsing + +* {% jdoc core::RuleSetFactory %} and {% jdoc core::RuleSetFactoryUtils %} have been deprecated in favor of {% jdoc core::RuleSetLoader %}. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. +* Some static creation methods have been added to {% jdoc core::RuleSet %} for simple cases, eg {% jdoc core::RuleSet#forSingleRule(core::Rule) %}. These replace some counterparts in {% jdoc core::RuleSetFactory %} +* Since {% jdoc core::RuleSets %} is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. +* {% jdoc core::RuleSetReferenceId %}, {% jdoc core::RuleSetReference %}, {% jdoc core::RuleSetFactoryCompatibility %} are deprecated. They are most likely not relevant outside of the implementation of pmd-core. + +###### Around the `PMD` class + +Many classes around PMD's entry point ({% jdoc core::PMD %}) have been deprecated as internal, including: +* The contents of the packages {% jdoc_package core::cli %}, {% jdoc_package core::processor %} +* {% jdoc core::SourceCodeProcessor %} +* The constructors of {% jdoc core::PMD %} (the class will be made a utility class) + +###### Miscellaneous + +* {% jdoc !!java::lang.java.ast.ASTPackageDeclaration#getPackageNameImage() %}, + {% 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 + +Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0. +You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning. + +* {% jdoc !!javascript::lang.ecmascript.Ecmascript3Handler %} +* {% jdoc !!javascript::lang.ecmascript.Ecmascript3Parser %} +* {% jdoc !!javascript::lang.ecmascript.ast.EcmascriptParser#parserOptions %} +* {% jdoc !!javascript::lang.ecmascript.ast.EcmascriptParser#getSuppressMap() %} +* {% jdoc !!core::lang.rule.ParametricRuleViolation %} +* {% jdoc !!core::lang.ParserOptions#suppressMarker %} +* {% jdoc !!modelica::lang.modelica.rule.ModelicaRuleViolationFactory %} + #### 6.29.0 No changes. diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index f39d4dec64..d360f4e9aa 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -103,4 +103,10 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr * [#2945](https://github.com/pmd/pmd/pull/2945): \[cs] Add option to ignore sequences of literals - [Maikel Steneker](https://github.com/maikelsteneker) * [#2962](https://github.com/pmd/pmd/pull/2962): \[cpp] Add support for C++ 14 binary literals - [Maikel Steneker](https://github.com/maikelsteneker) +### Stats +* 190 commits +* 25 closed tickets & PRs +* Days since last release: 49 + {% endtocmaker %} + From d2e4fb4ca370e7d5612dcc96fb74c29767a7671e Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 12 Dec 2020 09:42:02 +0100 Subject: [PATCH 57/98] [maven-release-plugin] prepare release pmd_releases/6.30.0 --- pmd-apex-jorje/pom.xml | 2 +- pmd-apex/pom.xml | 2 +- pmd-core/pom.xml | 2 +- pmd-cpp/pom.xml | 2 +- pmd-cs/pom.xml | 2 +- pmd-dart/pom.xml | 2 +- pmd-dist/pom.xml | 2 +- pmd-doc/pom.xml | 2 +- pmd-fortran/pom.xml | 2 +- pmd-go/pom.xml | 2 +- pmd-groovy/pom.xml | 2 +- pmd-java/pom.xml | 2 +- pmd-java8/pom.xml | 2 +- pmd-javascript/pom.xml | 2 +- pmd-jsp/pom.xml | 2 +- pmd-kotlin/pom.xml | 2 +- pmd-lang-test/pom.xml | 2 +- pmd-lua/pom.xml | 2 +- pmd-matlab/pom.xml | 2 +- pmd-modelica/pom.xml | 2 +- pmd-objectivec/pom.xml | 2 +- pmd-perl/pom.xml | 2 +- pmd-php/pom.xml | 2 +- pmd-plsql/pom.xml | 2 +- pmd-python/pom.xml | 2 +- pmd-ruby/pom.xml | 2 +- pmd-scala-modules/pmd-scala-common/pom.xml | 2 +- pmd-scala-modules/pmd-scala_2.12/pom.xml | 2 +- pmd-scala-modules/pmd-scala_2.13/pom.xml | 2 +- pmd-scala/pom.xml | 2 +- pmd-swift/pom.xml | 2 +- pmd-test/pom.xml | 2 +- pmd-visualforce/pom.xml | 2 +- pmd-vm/pom.xml | 2 +- pmd-xml/pom.xml | 2 +- pom.xml | 6 +++--- 36 files changed, 38 insertions(+), 38 deletions(-) diff --git a/pmd-apex-jorje/pom.xml b/pmd-apex-jorje/pom.xml index 9f97ef4389..8b4e00aadf 100644 --- a/pmd-apex-jorje/pom.xml +++ b/pmd-apex-jorje/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index cb2ae54b65..4d976873d9 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index 1046e15412..f2d79ac028 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-cpp/pom.xml b/pmd-cpp/pom.xml index 1946fd4b5a..f418dd083e 100644 --- a/pmd-cpp/pom.xml +++ b/pmd-cpp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-cs/pom.xml b/pmd-cs/pom.xml index 86ea909c53..957bcee0bc 100644 --- a/pmd-cs/pom.xml +++ b/pmd-cs/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-dart/pom.xml b/pmd-dart/pom.xml index 34d8fb11be..4966fdcd84 100644 --- a/pmd-dart/pom.xml +++ b/pmd-dart/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index a979c55bda..48d55ccced 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-doc/pom.xml b/pmd-doc/pom.xml index 25621b7967..e529d6f30e 100644 --- a/pmd-doc/pom.xml +++ b/pmd-doc/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-fortran/pom.xml b/pmd-fortran/pom.xml index 9787233fa4..0d27470032 100644 --- a/pmd-fortran/pom.xml +++ b/pmd-fortran/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-go/pom.xml b/pmd-go/pom.xml index f499309565..cc9b3fc521 100644 --- a/pmd-go/pom.xml +++ b/pmd-go/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-groovy/pom.xml b/pmd-groovy/pom.xml index de6f4770b0..eb3a6902c9 100644 --- a/pmd-groovy/pom.xml +++ b/pmd-groovy/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index f3f22fc26f..33a5128cec 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-java8/pom.xml b/pmd-java8/pom.xml index 39b093748d..8247ce93c4 100644 --- a/pmd-java8/pom.xml +++ b/pmd-java8/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml index c6a0dab926..ef367b764f 100644 --- a/pmd-javascript/pom.xml +++ b/pmd-javascript/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-jsp/pom.xml b/pmd-jsp/pom.xml index a11e5ad3c0..4f9d0917ce 100644 --- a/pmd-jsp/pom.xml +++ b/pmd-jsp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-kotlin/pom.xml b/pmd-kotlin/pom.xml index 0471f85ed6..9c5f3356b6 100644 --- a/pmd-kotlin/pom.xml +++ b/pmd-kotlin/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-lang-test/pom.xml b/pmd-lang-test/pom.xml index 1f63a2c9ad..a8df76ac34 100644 --- a/pmd-lang-test/pom.xml +++ b/pmd-lang-test/pom.xml @@ -12,7 +12,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-lua/pom.xml b/pmd-lua/pom.xml index 6618d50ab6..50866adfa3 100644 --- a/pmd-lua/pom.xml +++ b/pmd-lua/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-matlab/pom.xml b/pmd-matlab/pom.xml index 4b510c7b5c..4ad87f9038 100644 --- a/pmd-matlab/pom.xml +++ b/pmd-matlab/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-modelica/pom.xml b/pmd-modelica/pom.xml index 7e000aec1a..328a0a0eac 100644 --- a/pmd-modelica/pom.xml +++ b/pmd-modelica/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-objectivec/pom.xml b/pmd-objectivec/pom.xml index eb05bd86d3..54415bd725 100644 --- a/pmd-objectivec/pom.xml +++ b/pmd-objectivec/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-perl/pom.xml b/pmd-perl/pom.xml index a4b20c2485..1447c71e3d 100644 --- a/pmd-perl/pom.xml +++ b/pmd-perl/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-php/pom.xml b/pmd-php/pom.xml index 53370954f8..1a7a08c827 100644 --- a/pmd-php/pom.xml +++ b/pmd-php/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-plsql/pom.xml b/pmd-plsql/pom.xml index b11a021f61..c08273fd43 100644 --- a/pmd-plsql/pom.xml +++ b/pmd-plsql/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-python/pom.xml b/pmd-python/pom.xml index 148cb89aaf..428ad05614 100644 --- a/pmd-python/pom.xml +++ b/pmd-python/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-ruby/pom.xml b/pmd-ruby/pom.xml index 7e3bd5faff..bf6ea9ad0b 100644 --- a/pmd-ruby/pom.xml +++ b/pmd-ruby/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-scala-modules/pmd-scala-common/pom.xml b/pmd-scala-modules/pmd-scala-common/pom.xml index eba47ba601..1a1a83f2b9 100644 --- a/pmd-scala-modules/pmd-scala-common/pom.xml +++ b/pmd-scala-modules/pmd-scala-common/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../.. diff --git a/pmd-scala-modules/pmd-scala_2.12/pom.xml b/pmd-scala-modules/pmd-scala_2.12/pom.xml index 20122bc23d..4c239057ed 100644 --- a/pmd-scala-modules/pmd-scala_2.12/pom.xml +++ b/pmd-scala-modules/pmd-scala_2.12/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd-scala-common - 6.30.0-SNAPSHOT + 6.30.0 ../pmd-scala-common diff --git a/pmd-scala-modules/pmd-scala_2.13/pom.xml b/pmd-scala-modules/pmd-scala_2.13/pom.xml index 1f256859a7..13e59015ca 100644 --- a/pmd-scala-modules/pmd-scala_2.13/pom.xml +++ b/pmd-scala-modules/pmd-scala_2.13/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd-scala-common - 6.30.0-SNAPSHOT + 6.30.0 ../pmd-scala-common diff --git a/pmd-scala/pom.xml b/pmd-scala/pom.xml index 97c49136d2..a91961580a 100644 --- a/pmd-scala/pom.xml +++ b/pmd-scala/pom.xml @@ -9,7 +9,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-swift/pom.xml b/pmd-swift/pom.xml index 4e3ac90b76..eb66c3f919 100644 --- a/pmd-swift/pom.xml +++ b/pmd-swift/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-test/pom.xml b/pmd-test/pom.xml index 1106159265..50a8858c20 100644 --- a/pmd-test/pom.xml +++ b/pmd-test/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-visualforce/pom.xml b/pmd-visualforce/pom.xml index 6aba63770d..8e610ed8b6 100644 --- a/pmd-visualforce/pom.xml +++ b/pmd-visualforce/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-vm/pom.xml b/pmd-vm/pom.xml index 89b9c6d6f1..4f24500516 100644 --- a/pmd-vm/pom.xml +++ b/pmd-vm/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pmd-xml/pom.xml b/pmd-xml/pom.xml index 3392c50697..1496991414 100644 --- a/pmd-xml/pom.xml +++ b/pmd-xml/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 ../ diff --git a/pom.xml b/pom.xml index ba793f9856..45665ddc3d 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 net.sourceforge.pmd pmd - 6.30.0-SNAPSHOT + 6.30.0 pom PMD @@ -55,7 +55,7 @@ scm:git:git://github.com/pmd/pmd.git scm:git:ssh://git@github.com/pmd/pmd.git https://github.com/pmd/pmd - HEAD + pmd_releases/6.30.0 @@ -76,7 +76,7 @@ - 2020-10-24T08:17:24Z + 2020-12-12T08:31:18Z 7 From d284ea96343bb9339e3385401505c5210668b169 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 12 Dec 2020 09:42:10 +0100 Subject: [PATCH 58/98] [maven-release-plugin] prepare for next development iteration --- pmd-apex-jorje/pom.xml | 2 +- pmd-apex/pom.xml | 2 +- pmd-core/pom.xml | 2 +- pmd-cpp/pom.xml | 2 +- pmd-cs/pom.xml | 2 +- pmd-dart/pom.xml | 2 +- pmd-dist/pom.xml | 2 +- pmd-doc/pom.xml | 2 +- pmd-fortran/pom.xml | 2 +- pmd-go/pom.xml | 2 +- pmd-groovy/pom.xml | 2 +- pmd-java/pom.xml | 2 +- pmd-java8/pom.xml | 2 +- pmd-javascript/pom.xml | 2 +- pmd-jsp/pom.xml | 2 +- pmd-kotlin/pom.xml | 2 +- pmd-lang-test/pom.xml | 2 +- pmd-lua/pom.xml | 2 +- pmd-matlab/pom.xml | 2 +- pmd-modelica/pom.xml | 2 +- pmd-objectivec/pom.xml | 2 +- pmd-perl/pom.xml | 2 +- pmd-php/pom.xml | 2 +- pmd-plsql/pom.xml | 2 +- pmd-python/pom.xml | 2 +- pmd-ruby/pom.xml | 2 +- pmd-scala-modules/pmd-scala-common/pom.xml | 2 +- pmd-scala-modules/pmd-scala_2.12/pom.xml | 2 +- pmd-scala-modules/pmd-scala_2.13/pom.xml | 2 +- pmd-scala/pom.xml | 2 +- pmd-swift/pom.xml | 2 +- pmd-test/pom.xml | 2 +- pmd-visualforce/pom.xml | 2 +- pmd-vm/pom.xml | 2 +- pmd-xml/pom.xml | 2 +- pom.xml | 6 +++--- 36 files changed, 38 insertions(+), 38 deletions(-) diff --git a/pmd-apex-jorje/pom.xml b/pmd-apex-jorje/pom.xml index 8b4e00aadf..e751b1785e 100644 --- a/pmd-apex-jorje/pom.xml +++ b/pmd-apex-jorje/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index 4d976873d9..268aa9ec98 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index f2d79ac028..a313f1c309 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-cpp/pom.xml b/pmd-cpp/pom.xml index f418dd083e..217ee661f8 100644 --- a/pmd-cpp/pom.xml +++ b/pmd-cpp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-cs/pom.xml b/pmd-cs/pom.xml index 957bcee0bc..e850c8e190 100644 --- a/pmd-cs/pom.xml +++ b/pmd-cs/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-dart/pom.xml b/pmd-dart/pom.xml index 4966fdcd84..162da6a369 100644 --- a/pmd-dart/pom.xml +++ b/pmd-dart/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index 48d55ccced..277fb54ec8 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-doc/pom.xml b/pmd-doc/pom.xml index e529d6f30e..220e8cc4f6 100644 --- a/pmd-doc/pom.xml +++ b/pmd-doc/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-fortran/pom.xml b/pmd-fortran/pom.xml index 0d27470032..a7849a888b 100644 --- a/pmd-fortran/pom.xml +++ b/pmd-fortran/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-go/pom.xml b/pmd-go/pom.xml index cc9b3fc521..5cb6b21da1 100644 --- a/pmd-go/pom.xml +++ b/pmd-go/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-groovy/pom.xml b/pmd-groovy/pom.xml index eb3a6902c9..81280f254f 100644 --- a/pmd-groovy/pom.xml +++ b/pmd-groovy/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index 33a5128cec..30275fa027 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-java8/pom.xml b/pmd-java8/pom.xml index 8247ce93c4..97d9206b84 100644 --- a/pmd-java8/pom.xml +++ b/pmd-java8/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml index ef367b764f..7cd4b028d3 100644 --- a/pmd-javascript/pom.xml +++ b/pmd-javascript/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-jsp/pom.xml b/pmd-jsp/pom.xml index 4f9d0917ce..85e5891550 100644 --- a/pmd-jsp/pom.xml +++ b/pmd-jsp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-kotlin/pom.xml b/pmd-kotlin/pom.xml index 9c5f3356b6..ad5e204d58 100644 --- a/pmd-kotlin/pom.xml +++ b/pmd-kotlin/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-lang-test/pom.xml b/pmd-lang-test/pom.xml index a8df76ac34..acf724c4f3 100644 --- a/pmd-lang-test/pom.xml +++ b/pmd-lang-test/pom.xml @@ -12,7 +12,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-lua/pom.xml b/pmd-lua/pom.xml index 50866adfa3..318d4243fe 100644 --- a/pmd-lua/pom.xml +++ b/pmd-lua/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-matlab/pom.xml b/pmd-matlab/pom.xml index 4ad87f9038..3fadbc21b1 100644 --- a/pmd-matlab/pom.xml +++ b/pmd-matlab/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-modelica/pom.xml b/pmd-modelica/pom.xml index 328a0a0eac..1dad77e3ab 100644 --- a/pmd-modelica/pom.xml +++ b/pmd-modelica/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-objectivec/pom.xml b/pmd-objectivec/pom.xml index 54415bd725..dad8ef3694 100644 --- a/pmd-objectivec/pom.xml +++ b/pmd-objectivec/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-perl/pom.xml b/pmd-perl/pom.xml index 1447c71e3d..5c6f67d9f8 100644 --- a/pmd-perl/pom.xml +++ b/pmd-perl/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-php/pom.xml b/pmd-php/pom.xml index 1a7a08c827..262882fdf1 100644 --- a/pmd-php/pom.xml +++ b/pmd-php/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-plsql/pom.xml b/pmd-plsql/pom.xml index c08273fd43..302036b5ae 100644 --- a/pmd-plsql/pom.xml +++ b/pmd-plsql/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-python/pom.xml b/pmd-python/pom.xml index 428ad05614..7d0242adbf 100644 --- a/pmd-python/pom.xml +++ b/pmd-python/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-ruby/pom.xml b/pmd-ruby/pom.xml index bf6ea9ad0b..5b7eff715d 100644 --- a/pmd-ruby/pom.xml +++ b/pmd-ruby/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-scala-modules/pmd-scala-common/pom.xml b/pmd-scala-modules/pmd-scala-common/pom.xml index 1a1a83f2b9..c64d9a09da 100644 --- a/pmd-scala-modules/pmd-scala-common/pom.xml +++ b/pmd-scala-modules/pmd-scala-common/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../.. diff --git a/pmd-scala-modules/pmd-scala_2.12/pom.xml b/pmd-scala-modules/pmd-scala_2.12/pom.xml index 4c239057ed..3cb91af75f 100644 --- a/pmd-scala-modules/pmd-scala_2.12/pom.xml +++ b/pmd-scala-modules/pmd-scala_2.12/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd-scala-common - 6.30.0 + 6.31.0-SNAPSHOT ../pmd-scala-common diff --git a/pmd-scala-modules/pmd-scala_2.13/pom.xml b/pmd-scala-modules/pmd-scala_2.13/pom.xml index 13e59015ca..192cd100fa 100644 --- a/pmd-scala-modules/pmd-scala_2.13/pom.xml +++ b/pmd-scala-modules/pmd-scala_2.13/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd-scala-common - 6.30.0 + 6.31.0-SNAPSHOT ../pmd-scala-common diff --git a/pmd-scala/pom.xml b/pmd-scala/pom.xml index a91961580a..c9a1ed0d6b 100644 --- a/pmd-scala/pom.xml +++ b/pmd-scala/pom.xml @@ -9,7 +9,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-swift/pom.xml b/pmd-swift/pom.xml index eb66c3f919..3aebee81e7 100644 --- a/pmd-swift/pom.xml +++ b/pmd-swift/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-test/pom.xml b/pmd-test/pom.xml index 50a8858c20..a05d3058ef 100644 --- a/pmd-test/pom.xml +++ b/pmd-test/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-visualforce/pom.xml b/pmd-visualforce/pom.xml index 8e610ed8b6..94448ea109 100644 --- a/pmd-visualforce/pom.xml +++ b/pmd-visualforce/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-vm/pom.xml b/pmd-vm/pom.xml index 4f24500516..65a85d82d5 100644 --- a/pmd-vm/pom.xml +++ b/pmd-vm/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pmd-xml/pom.xml b/pmd-xml/pom.xml index 1496991414..40ce8c631b 100644 --- a/pmd-xml/pom.xml +++ b/pmd-xml/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT ../ diff --git a/pom.xml b/pom.xml index 45665ddc3d..d7c11a0bda 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 net.sourceforge.pmd pmd - 6.30.0 + 6.31.0-SNAPSHOT pom PMD @@ -55,7 +55,7 @@ scm:git:git://github.com/pmd/pmd.git scm:git:ssh://git@github.com/pmd/pmd.git https://github.com/pmd/pmd - pmd_releases/6.30.0 + HEAD @@ -76,7 +76,7 @@ - 2020-12-12T08:31:18Z + 2020-12-12T08:42:10Z 7 From 06dae58f78bd25f1f13c880069ca13bfbe983365 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 12 Dec 2020 09:51:21 +0100 Subject: [PATCH 59/98] Prepare next development version --- docs/_config.yml | 6 +- docs/pages/release_notes.md | 88 ------------------------ docs/pages/release_notes_old.md | 117 ++++++++++++++++++++++++++++++++ 3 files changed, 120 insertions(+), 91 deletions(-) diff --git a/docs/_config.yml b/docs/_config.yml index d28e322762..220abe44ad 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,9 +1,9 @@ repository: pmd/pmd pmd: - version: 6.30.0 - previous_version: 6.29.0 - date: 12-December-2020 + version: 6.31.0-SNAPSHOT + previous_version: 6.30.0 + date: ??-January-2021 release_type: minor # release types: major, minor, bugfix diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index d360f4e9aa..b8f8783555 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -14,99 +14,11 @@ This is a {{ site.pmd.release_type }} release. ### New and noteworthy -##### CPD - -* The C# module now supports the new option [`--ignore-literal-sequences`](https://pmd.github.io/latest/pmd_userdocs_cpd.html#-ignore-literal-sequences), which can be used to avoid detection of some uninteresting clones. Support for other languages may be added in the future. See [#2945](https://github.com/pmd/pmd/pull/2945) - -* The Scala module now supports [suppression](https://pmd.github.io/latest/pmd_userdocs_cpd.html#suppression) through `CPD-ON`/`CPD-OFF` comment pairs. See [#2929](https://github.com/pmd/pmd/pull/2929) - - -##### Type information for VisualForce - -The Visualforce AST now can resolve the data type of Visualforce expressions that reference Apex Controller properties and Custom Object fields. This feature improves the precision of existing rules, like {% rule vf/security/VfUnescapeEl %}. - -This can be configured using two environment variables: -* `PMD_VF_APEXDIRECTORIES`: Comma separated list of directories for Apex classes. Absolute or relative to the Visualforce directory. Default is `../classes`. Specifying an empty string will disable data type resolution for Apex Controller properties. -* `PMD_VF_OBJECTSDIRECTORIES`: Comma separated list of directories for Custom Objects. Absolute or relative to the Visualforce directory. Default is `../objects`. Specifying an empty string will disable data type resolution for Custom Object fields. - -This feature is experimental, in particular, expect changes to the way the configuration is specified. We'll probably extend the CLI instead of relying on environment variables in a future version. - -Thanks to Jeff Bartolotta and Roopa Mohan for contributing this! - ### Fixed Issues -* core - * [#1939](https://github.com/pmd/pmd/issues/1939): \[core] XPath expressions return handling - * [#1961](https://github.com/pmd/pmd/issues/1961): \[core] Text renderer should include name of violated rule - * [#2874](https://github.com/pmd/pmd/pull/2874): \[core] Fix XMLRenderer with UTF-16 -* cs - * [#2938](https://github.com/pmd/pmd/pull/2938): \[cs] CPD: ignoring using directives could not be disabled -* java - * [#2911](https://github.com/pmd/pmd/issues/2911): \[java] `ClassTypeResolver#searchNodeNameForClass` leaks memory - * [#2934](https://github.com/pmd/pmd/pull/2934): \[java] CompareObjectsWithEquals / UseEqualsToCompareStrings - False negatives with fields - * [#2940](https://github.com/pmd/pmd/pull/2940): \[java] Catch additional TypeNotPresentExceptions / LinkageErrors -* scala - * [#2480](https://github.com/pmd/pmd/issues/2480): \[scala] Support CPD suppressions - - ### API Changes -#### Deprecated API - - -##### Around RuleSet parsing - -* {% jdoc core::RuleSetFactory %} and {% jdoc core::RuleSetFactoryUtils %} have been deprecated in favor of {% jdoc core::RuleSetLoader %}. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. -* Some static creation methods have been added to {% jdoc core::RuleSet %} for simple cases, eg {% jdoc core::RuleSet#forSingleRule(core::Rule) %}. These replace some counterparts in {% jdoc core::RuleSetFactory %} -* Since {% jdoc core::RuleSets %} is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. -* {% jdoc core::RuleSetReferenceId %}, {% jdoc core::RuleSetReference %}, {% jdoc core::RuleSetFactoryCompatibility %} are deprecated. They are most likely not relevant outside of the implementation of pmd-core. - -##### Around the `PMD` class - -Many classes around PMD's entry point ({% jdoc core::PMD %}) have been deprecated as internal, including: -* The contents of the packages {% jdoc_package core::cli %}, {% jdoc_package core::processor %} -* {% jdoc core::SourceCodeProcessor %} -* The constructors of {% jdoc core::PMD %} (the class will be made a utility class) - -##### Miscellaneous - -* {% jdoc !!java::lang.java.ast.ASTPackageDeclaration#getPackageNameImage() %}, - {% 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 - -Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0. -You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning. - -* {% jdoc !!javascript::lang.ecmascript.Ecmascript3Handler %} -* {% jdoc !!javascript::lang.ecmascript.Ecmascript3Parser %} -* {% jdoc !!javascript::lang.ecmascript.ast.EcmascriptParser#parserOptions %} -* {% jdoc !!javascript::lang.ecmascript.ast.EcmascriptParser#getSuppressMap() %} -* {% jdoc !!core::lang.rule.ParametricRuleViolation %} -* {% jdoc !!core::lang.ParserOptions#suppressMarker %} -* {% jdoc !!modelica::lang.modelica.rule.ModelicaRuleViolationFactory %} - - ### External Contributions -* [#2864](https://github.com/pmd/pmd/pull/2864): [vf] Provide expression type information to Visualforce rules to avoid false positives - [Jeff Bartolotta](https://github.com/jbartolotta-sfdc) -* [#2914](https://github.com/pmd/pmd/pull/2914): \[core] Include rule name in text renderer - [Gunther Schrijvers](https://github.com/GuntherSchrijvers) -* [#2925](https://github.com/pmd/pmd/pull/2925): Cleanup: Correct annotation array initializer indents from checkstyle #8083 - [Abhishek Kumar](https://github.com/Abhishek-kumar09) -* [#2929](https://github.com/pmd/pmd/pull/2929): \[scala] Add support for CPD-ON and CPD-OFF special comments - [Andy Robinson](https://github.com/andyrobinson) -* [#2936](https://github.com/pmd/pmd/pull/2936): \[java] (doc) Fix typo: "an accessor" not "a" - [Igor Moreno](https://github.com/igormoreno) -* [#2938](https://github.com/pmd/pmd/pull/2938): \[cs] CPD: fix issue where ignoring using directives could not be disabled - [Maikel Steneker](https://github.com/maikelsteneker) -* [#2945](https://github.com/pmd/pmd/pull/2945): \[cs] Add option to ignore sequences of literals - [Maikel Steneker](https://github.com/maikelsteneker) -* [#2962](https://github.com/pmd/pmd/pull/2962): \[cpp] Add support for C++ 14 binary literals - [Maikel Steneker](https://github.com/maikelsteneker) - -### Stats -* 190 commits -* 25 closed tickets & PRs -* Days since last release: 49 - {% endtocmaker %} diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index a02971b7c0..9191d5bd41 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -5,6 +5,123 @@ permalink: pmd_release_notes_old.html Previous versions of PMD can be downloaded here: https://github.com/pmd/pmd/releases +## 12-December-2020 - 6.30.0 + +The PMD team is pleased to announce PMD 6.30.0. + +This is a minor release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [CPD](#cpd) + * [Type information for VisualForce](#type-information-for-visualforce) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) + * [Deprecated API](#deprecated-api) + * [Around RuleSet parsing](#around-ruleset-parsing) + * [Around the `PMD` class](#around-the-`pmd`-class) + * [Miscellaneous](#miscellaneous) + * [Internal API](#internal-api) +* [External Contributions](#external-contributions) +* [Stats](#stats) + +### New and noteworthy + +##### CPD + +* The C# module now supports the new option [`--ignore-literal-sequences`](https://pmd.github.io/latest/pmd_userdocs_cpd.html#-ignore-literal-sequences), which can be used to avoid detection of some uninteresting clones. Support for other languages may be added in the future. See [#2945](https://github.com/pmd/pmd/pull/2945) + +* The Scala module now supports [suppression](https://pmd.github.io/latest/pmd_userdocs_cpd.html#suppression) through `CPD-ON`/`CPD-OFF` comment pairs. See [#2929](https://github.com/pmd/pmd/pull/2929) + + +##### Type information for VisualForce + +The Visualforce AST now can resolve the data type of Visualforce expressions that reference Apex Controller properties and Custom Object fields. This feature improves the precision of existing rules, like [`VfUnescapeEl`](https://pmd.github.io/pmd-6.30.0/pmd_rules_vf_security.html#vfunescapeel). + +This can be configured using two environment variables: +* `PMD_VF_APEXDIRECTORIES`: Comma separated list of directories for Apex classes. Absolute or relative to the Visualforce directory. Default is `../classes`. Specifying an empty string will disable data type resolution for Apex Controller properties. +* `PMD_VF_OBJECTSDIRECTORIES`: Comma separated list of directories for Custom Objects. Absolute or relative to the Visualforce directory. Default is `../objects`. Specifying an empty string will disable data type resolution for Custom Object fields. + +This feature is experimental, in particular, expect changes to the way the configuration is specified. We'll probably extend the CLI instead of relying on environment variables in a future version. + +Thanks to Jeff Bartolotta and Roopa Mohan for contributing this! + +### Fixed Issues + +* core + * [#1939](https://github.com/pmd/pmd/issues/1939): \[core] XPath expressions return handling + * [#1961](https://github.com/pmd/pmd/issues/1961): \[core] Text renderer should include name of violated rule + * [#2874](https://github.com/pmd/pmd/pull/2874): \[core] Fix XMLRenderer with UTF-16 +* cs + * [#2938](https://github.com/pmd/pmd/pull/2938): \[cs] CPD: ignoring using directives could not be disabled +* java + * [#2911](https://github.com/pmd/pmd/issues/2911): \[java] `ClassTypeResolver#searchNodeNameForClass` leaks memory + * [#2934](https://github.com/pmd/pmd/pull/2934): \[java] CompareObjectsWithEquals / UseEqualsToCompareStrings - False negatives with fields + * [#2940](https://github.com/pmd/pmd/pull/2940): \[java] Catch additional TypeNotPresentExceptions / LinkageErrors +* scala + * [#2480](https://github.com/pmd/pmd/issues/2480): \[scala] Support CPD suppressions + + +### API Changes + +#### Deprecated API + + +##### Around RuleSet parsing + +* RuleSetFactory and RuleSetFactoryUtils have been deprecated in favor of RuleSetLoader. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. +* Some static creation methods have been added to RuleSet for simple cases, eg forSingleRule. These replace some counterparts in RuleSetFactory +* Since RuleSets is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. +* RuleSetReferenceId, RuleSetReference, RuleSetFactoryCompatibility are deprecated. They are most likely not relevant outside of the implementation of pmd-core. + +##### Around the `PMD` class + +Many classes around PMD's entry point (PMD) have been deprecated as internal, including: +* The contents of the packages net.sourceforge.pmd.cli, net.sourceforge.pmd.processor +* SourceCodeProcessor +* The constructors of PMD (the class will be made a utility class) + +##### Miscellaneous + +* ASTPackageDeclaration#getPackageNameImage, + ASTTypeParameter#getParameterName + and the corresponding XPath attributes. In both cases they're replaced with a new method `getName`, + the attribute is `@Name`. +* ASTClassOrInterfaceBody#isAnonymousInnerClass, + and ASTClassOrInterfaceBody#isEnumChild, + refs [#905](https://github.com/pmd/pmd/issues/905) + +#### Internal API + +Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0. +You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning. + +* net.sourceforge.pmd.lang.ecmascript.Ecmascript3Handler +* net.sourceforge.pmd.lang.ecmascript.Ecmascript3Parser +* EcmascriptParser#parserOptions +* EcmascriptParser#getSuppressMap +* net.sourceforge.pmd.lang.rule.ParametricRuleViolation +* ParserOptions#suppressMarker +* net.sourceforge.pmd.lang.modelica.rule.ModelicaRuleViolationFactory + + +### External Contributions + +* [#2864](https://github.com/pmd/pmd/pull/2864): [vf] Provide expression type information to Visualforce rules to avoid false positives - [Jeff Bartolotta](https://github.com/jbartolotta-sfdc) +* [#2914](https://github.com/pmd/pmd/pull/2914): \[core] Include rule name in text renderer - [Gunther Schrijvers](https://github.com/GuntherSchrijvers) +* [#2925](https://github.com/pmd/pmd/pull/2925): Cleanup: Correct annotation array initializer indents from checkstyle #8083 - [Abhishek Kumar](https://github.com/Abhishek-kumar09) +* [#2929](https://github.com/pmd/pmd/pull/2929): \[scala] Add support for CPD-ON and CPD-OFF special comments - [Andy Robinson](https://github.com/andyrobinson) +* [#2936](https://github.com/pmd/pmd/pull/2936): \[java] (doc) Fix typo: "an accessor" not "a" - [Igor Moreno](https://github.com/igormoreno) +* [#2938](https://github.com/pmd/pmd/pull/2938): \[cs] CPD: fix issue where ignoring using directives could not be disabled - [Maikel Steneker](https://github.com/maikelsteneker) +* [#2945](https://github.com/pmd/pmd/pull/2945): \[cs] Add option to ignore sequences of literals - [Maikel Steneker](https://github.com/maikelsteneker) +* [#2962](https://github.com/pmd/pmd/pull/2962): \[cpp] Add support for C++ 14 binary literals - [Maikel Steneker](https://github.com/maikelsteneker) + +### Stats +* 190 commits +* 25 closed tickets & PRs +* Days since last release: 49 + ## 24-October-2020 - 6.29.0 The PMD team is pleased to announce PMD 6.29.0. From 139a3f6103a0297409185e7c0cf598e74e4fa58a Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 12 Dec 2020 11:19:23 +0100 Subject: [PATCH 60/98] [ci] Fix build in trunk (#2967) The same problem would have occurred for the release build. --- .ci/inc/maven-dependencies.inc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.ci/inc/maven-dependencies.inc b/.ci/inc/maven-dependencies.inc index 0aaa020b81..c99279b395 100644 --- a/.ci/inc/maven-dependencies.inc +++ b/.ci/inc/maven-dependencies.inc @@ -20,7 +20,18 @@ function maven_dependencies_resolve() { dokka_version=$(./mvnw -q -Dexec.executable="echo" -Dexec.args='${dokka.version}' \ --non-recursive org.codehaus.mojo:exec-maven-plugin:3.0.0:exec) - ./mvnw dependency:resolve + # build first the modules, that have dependencies between themselves + # first build pmd-lang-test, pmd-test and pmd-core - used by all modules + ./mvnw clean install -pl pmd-core,pmd-test,pmd-lang-test -DskipTests -Dpmd.skip=true \ + -Dcheckstyle.skip=true -Dmaven.javadoc.skip=true -Dmaven.source.skip=true + # then build dependencies for pmd-visualforce needs: pmd-apex->pmd-apex-jorje+pmd-test+pmd-core + ./mvnw clean install -pl pmd-core,pmd-test,pmd-lang-test,pmd-apex-jorje,pmd-apex -DskipTests -Dpmd.skip=true \ + -Dcheckstyle.skip=true -Dmaven.javadoc.skip=true -Dmaven.source.skip=true + + # the resolve most other projects. The excluded projects depend on other projects in the reactor, which is not + # completely built yet, so these are excluded. + ./mvnw dependency:resolve -pl '!pmd-dist,!pmd-java8,!pmd-doc,!pmd-scala' + ./mvnw dependency:get -DgroupId=org.jetbrains.dokka \ -DartifactId=dokka-maven-plugin \ -Dversion=${dokka_version} \ From f276eec0ba1a0346914580712ac75913ddc132d9 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 12 Dec 2020 11:19:51 +0100 Subject: [PATCH 61/98] [ci] Use ruby 2.7 for release build (#2967) --- .github/workflows/releases.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/releases.yml b/.github/workflows/releases.yml index 2948cb6653..9e96a7f1ac 100644 --- a/.github/workflows/releases.yml +++ b/.github/workflows/releases.yml @@ -10,6 +10,10 @@ jobs: continue-on-error: false steps: - uses: actions/checkout@v2 + - name: Set up Ruby 2.7 + uses: actions/setup-ruby@v1 + with: + ruby-version: 2.7 - name: Check Environment run: .ci/check-environment.sh shell: bash From ba0e9ec7d18b187da7c08c558aba24e330f6574c Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 12 Dec 2020 11:29:36 +0100 Subject: [PATCH 62/98] [ci] Install needed gems before rendering release notes (#2967) --- .ci/build.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.ci/build.sh b/.ci/build.sh index d6475b70a1..f7651bff23 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -181,6 +181,10 @@ function pmd_ci_build_and_upload_doc() { pmd_code_removeJavadoc "${VERSION}-SNAPSHOT" # updating github release text + rm -f .bundle/config + bundle config set --local path vendor/bundle + bundle config set --local with release_notes_preprocessing + bundle install # renders, and skips the first 6 lines - the Jekyll front-matter local rendered_release_notes=$(bundle exec .ci/render_release_notes.rb docs/pages/release_notes.md | tail -n +6) local release_name="PMD ${VERSION} ($(date -u +%d-%B-%Y))" From a672c5e85d28654be2d957c8248068c1f8d3826e Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 12 Dec 2020 11:46:39 +0100 Subject: [PATCH 63/98] [doc] Fix release notes --- docs/pages/release_notes_old.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index 9191d5bd41..cc2bb4a7f7 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -70,7 +70,7 @@ Thanks to Jeff Bartolotta and Roopa Mohan for contributing this! ##### Around RuleSet parsing -* RuleSetFactory and RuleSetFactoryUtils have been deprecated in favor of RuleSetLoader. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. +* RuleSetFactory and RulesetsFactoryUtils have been deprecated in favor of RuleSetLoader. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. * Some static creation methods have been added to RuleSet for simple cases, eg forSingleRule. These replace some counterparts in RuleSetFactory * Since RuleSets is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. * RuleSetReferenceId, RuleSetReference, RuleSetFactoryCompatibility are deprecated. They are most likely not relevant outside of the implementation of pmd-core. From 0488c853e3c43936e3149a426287fd8683655d5b Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 12 Dec 2020 12:06:59 +0100 Subject: [PATCH 64/98] [ci] Fix creating regression tester baseline for release builds (#2967) --- .ci/inc/regression-tester.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index ffa9d25ee0..e959e9a780 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -61,15 +61,17 @@ function regression_tester_uploadBaseline() { log_info "Generating and uploading baseline for pmdtester..." cd .. bundle config --local gemfile pmd/Gemfile + bundle config set --local path pmd/vendor/bundle bundle exec pmdtester \ --mode single \ --local-git-repo ./pmd \ - --patch-branch ${PMD_CI_BRANCH} \ + --patch-branch ${PMD_CI_BRANCH:-$PMD_CI_TAG} \ --patch-config ./pmd/.ci/files/all-java.xml \ --list-of-project ./pmd/.ci/files/project-list.xml --html-flag \ --error-recovery cd target/reports - BRANCH_FILENAME="${PMD_CI_BRANCH/\//_}" + BRANCH_FILENAME="${PMD_CI_BRANCH:-$PMD_CI_TAG}" + BRANCH_FILENAME="${BRANCH_FILENAME/\//_}" zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ # ssh-key for pmd-code.org is setup already by pmd_ci_setup_ssh scp ${BRANCH_FILENAME}-baseline.zip pmd@pmd-code.org:/httpdocs/pmd-regression-tester/ From 275e66d77a0f5f214fa4f159337f15e6a3429cb7 Mon Sep 17 00:00:00 2001 From: jr Date: Mon, 14 Dec 2020 14:36:51 -0800 Subject: [PATCH 65/98] [apex] New Rule: override equals and hashcode rule --- .../OverrideBothEqualsAndHashcodeRule.java | 66 +++++++ .../resources/category/apex/errorprone.xml | 34 ++++ .../OverrideBothEqualsAndHashcodeTest.java | 11 ++ .../xml/OverrideBothEqualsAndHashcode.xml | 166 ++++++++++++++++++ 4 files changed, 277 insertions(+) create mode 100644 pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java create mode 100644 pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java create mode 100644 pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java new file mode 100644 index 0000000000..5adf03c2b1 --- /dev/null +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java @@ -0,0 +1,66 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.rule.errorprone; + +import net.sourceforge.pmd.lang.apex.ast.ASTMethod; +import net.sourceforge.pmd.lang.apex.ast.ASTParameter; +import net.sourceforge.pmd.lang.apex.ast.ASTUserClass; +import net.sourceforge.pmd.lang.apex.ast.ApexNode; +import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule; + +public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule { + + @Override + public Object visit(ASTUserClass node, Object data) { + super.visit(node, data); + + ApexNode equalsNode = null; + ApexNode hashNode = null; + for (ASTMethod method : node.findChildrenOfType(ASTMethod.class)) { + if (equalsNode == null && isEquals(method)) { + equalsNode = method; + } + if (hashNode == null && isHashCode(method)) { + hashNode = method; + } + if (hashNode != null && equalsNode != null) { + break; + } + } + + if (equalsNode != null && hashNode == null) { + addViolation(data, equalsNode); + } else if (hashNode != null && equalsNode == null) { + addViolation(data, hashNode); + } + + return data; + } + + private Boolean isEquals(ASTMethod node) { + int numParams = 0; + String paramType = null; + for (int ix = 0; ix < node.getNumChildren(); ix++) { + ApexNode sn = node.getChild(ix); + if (sn instanceof ASTParameter) { + numParams++; + paramType = ((ASTParameter) sn).getType(); + } + } + return numParams == 1 && node.hasImageEqualTo("equals") && "Object".equalsIgnoreCase(paramType); + } + + private Boolean isHashCode(ASTMethod node) { + int numParams = 0; + for (int ix = 0; ix < node.getNumChildren(); ix++) { + ApexNode sn = node.getChild(ix); + if (sn instanceof ASTParameter) { + numParams++; + } + } + + return numParams == 0 && node.hasImageEqualTo("hashCode"); + } +} diff --git a/pmd-apex/src/main/resources/category/apex/errorprone.xml b/pmd-apex/src/main/resources/category/apex/errorprone.xml index 24d5483197..a9d56f5d04 100644 --- a/pmd-apex/src/main/resources/category/apex/errorprone.xml +++ b/pmd-apex/src/main/resources/category/apex/errorprone.xml @@ -291,6 +291,40 @@ public void bar(Integer a, Integer b) { // empty! } } +]]> + + + + + +Override both public boolean Object.equals(Object other), and public int Object.hashCode(), or override neither. Even if you are inheriting a hashCode() from a parent class, consider implementing hashCode and explicitly delegating to your superclass. + + 3 + + diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java new file mode 100644 index 0000000000..0b334a5f9f --- /dev/null +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeTest.java @@ -0,0 +1,11 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.rule.errorprone; + +import net.sourceforge.pmd.testframework.PmdRuleTst; + +public class OverrideBothEqualsAndHashcodeTest extends PmdRuleTst { + // no additional unit tests +} diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml new file mode 100644 index 0000000000..c5305e28e9 --- /dev/null +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml @@ -0,0 +1,166 @@ + + + + + hash code only + 1 + + + + + nested hash code only + 1 + + + + + equals only + 1 + + + + + nested equals only + 1 + + + + + overrides both + 0 + + + + + nested overrides both + 0 + + + + + overrides neither + 0 + + + + + equals sig uses String, not Object + 1 + + + + + interface + 0 + + + + + implements equals but with 2 args + 0 + + + + + overloaded hashCode + 0 + + + + + overloaded both + 0 + + + + + overloaded hashCode, should fail on equals + 1 + + + + + implements hashCode but with args + 0 + a) { + return 0; + } +} + ]]> + + From d0fb81de1f9eb616893f1c4b7f0bf63fc0982f1b Mon Sep 17 00:00:00 2001 From: Ozan Gulle Date: Wed, 16 Dec 2020 21:49:38 +0100 Subject: [PATCH 66/98] LiteralsFirstInComparisonsRule also checks for constants --- .../LiteralsFirstInComparisonsRule.java | 39 +++++++++++++++++-- .../xml/LiteralsFirstInComparisons.xml | 39 +++++++++++++++++++ 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java index 3419dd8d65..2c425eb570 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java @@ -19,6 +19,10 @@ import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { @@ -123,14 +127,15 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { private boolean isStringLiteralFirstArgumentOfSuffix(ASTPrimarySuffix primarySuffix) { try { - JavaNode firstArg = getFirstArgument(primarySuffix); - return isStringLiteral(firstArg); + JavaNode firstLiteralArg = getFirstLiteralArgument(primarySuffix); + JavaNode firstNameArg = getFirstNameArgument(primarySuffix); + return isStringLiteral(firstLiteralArg) || isConstantString(firstNameArg); } catch (NullPointerException e) { return false; } } - private JavaNode getFirstArgument(ASTPrimarySuffix primarySuffix) { + private JavaNode getFirstLiteralArgument(ASTPrimarySuffix primarySuffix) { ASTArguments arguments = primarySuffix.getFirstChildOfType(ASTArguments.class); ASTArgumentList argumentList = arguments.getFirstChildOfType(ASTArgumentList.class); ASTExpression expression = argumentList.getFirstChildOfType(ASTExpression.class); @@ -139,6 +144,15 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { return primaryPrefix.getFirstChildOfType(ASTLiteral.class); } + private JavaNode getFirstNameArgument(ASTPrimarySuffix primarySuffix) { + ASTArguments arguments = primarySuffix.getFirstChildOfType(ASTArguments.class); + ASTArgumentList argumentList = arguments.getFirstChildOfType(ASTArgumentList.class); + ASTExpression expression = argumentList.getFirstChildOfType(ASTExpression.class); + ASTPrimaryExpression primaryExpression = expression.getFirstChildOfType(ASTPrimaryExpression.class); + ASTPrimaryPrefix primaryPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); + return primaryPrefix.getFirstChildOfType(ASTName.class); + } + private boolean isStringLiteral(JavaNode node) { if (node instanceof ASTLiteral) { ASTLiteral literal = (ASTLiteral) node; @@ -147,6 +161,25 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { return false; } + private boolean isConstantString(JavaNode node) { + if (node instanceof ASTName) { + ASTName name = (ASTName) node; + ASTClassOrInterfaceBody classBody = name.getFirstParentOfType(ASTClassOrInterfaceBody.class); + ASTClassOrInterfaceBodyDeclaration classOrInterfaceBodyDeclaration = classBody.getFirstChildOfType(ASTClassOrInterfaceBodyDeclaration.class); + List fieldDeclarations = classOrInterfaceBodyDeclaration.findChildrenOfType(ASTFieldDeclaration.class); + for (ASTFieldDeclaration fieldDeclaration : fieldDeclarations) { + ASTVariableDeclarator declaration = fieldDeclaration.getFirstChildOfType(ASTVariableDeclarator.class); + if (declaration.getName().equals(name.getImage()) + && "class java.lang.String".equals(declaration.getType().toString()) + && fieldDeclaration.isFinal() + && fieldDeclaration.isStatic()) { + return true; + } + } + } + return false; + } + private boolean isNotWithinNullComparison(ASTPrimaryExpression node) { return !isWithinNullComparison(node); } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml index f76ba2658f..e1d2a48dbf 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml @@ -315,6 +315,45 @@ public class Foo { if (getStr("b").equals("ab")) { } // nok if ("ab".equals(getStr("b"))) { } // ok } +} + ]]> + + + + #575 PositionLiteralsFirstInComparisons to consider constant fields, i.e. static final Strings + 1 + + + + + #575 PositionLiteralsFirstInComparisons must not trigger if the field is not final + 0 + + + + + #575 PositionLiteralsFirstInComparisons must not trigger if the field is not static + 0 + From ae12a6db3542d6a491b3f750062b70b2a221668e Mon Sep 17 00:00:00 2001 From: Ozan Gulle Date: Wed, 16 Dec 2020 22:09:11 +0100 Subject: [PATCH 67/98] Put duplicated code in a separate method --- .../LiteralsFirstInComparisonsRule.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java index 2c425eb570..2c489a44fc 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java @@ -136,21 +136,19 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { } private JavaNode getFirstLiteralArgument(ASTPrimarySuffix primarySuffix) { - ASTArguments arguments = primarySuffix.getFirstChildOfType(ASTArguments.class); - ASTArgumentList argumentList = arguments.getFirstChildOfType(ASTArgumentList.class); - ASTExpression expression = argumentList.getFirstChildOfType(ASTExpression.class); - ASTPrimaryExpression primaryExpression = expression.getFirstChildOfType(ASTPrimaryExpression.class); - ASTPrimaryPrefix primaryPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); - return primaryPrefix.getFirstChildOfType(ASTLiteral.class); + return getArgumentPrimaryPrefixFromSuffix(primarySuffix).getFirstChildOfType(ASTLiteral.class); } private JavaNode getFirstNameArgument(ASTPrimarySuffix primarySuffix) { + return getArgumentPrimaryPrefixFromSuffix(primarySuffix).getFirstChildOfType(ASTName.class); + } + + private JavaNode getArgumentPrimaryPrefixFromSuffix(ASTPrimarySuffix primarySuffix) { ASTArguments arguments = primarySuffix.getFirstChildOfType(ASTArguments.class); ASTArgumentList argumentList = arguments.getFirstChildOfType(ASTArgumentList.class); ASTExpression expression = argumentList.getFirstChildOfType(ASTExpression.class); ASTPrimaryExpression primaryExpression = expression.getFirstChildOfType(ASTPrimaryExpression.class); - ASTPrimaryPrefix primaryPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); - return primaryPrefix.getFirstChildOfType(ASTName.class); + return primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); } private boolean isStringLiteral(JavaNode node) { From 129790ddfc6270c3592a51fae9dc0da0de001bcc Mon Sep 17 00:00:00 2001 From: Ozan Gulle Date: Wed, 16 Dec 2020 22:44:33 +0100 Subject: [PATCH 68/98] Fixed the order of imports --- .../bestpractices/LiteralsFirstInComparisonsRule.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java index 2c489a44fc..83200e98d6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java @@ -8,21 +8,21 @@ import java.util.List; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; import net.sourceforge.pmd.lang.java.ast.ASTArguments; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression; import net.sourceforge.pmd.lang.java.ast.ASTConditionalOrExpression; import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTLiteral; import net.sourceforge.pmd.lang.java.ast.ASTName; import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; +import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { From c37f208d3e9740825ddb8c91779499eb39e52bec Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 17 Dec 2020 18:31:13 +0100 Subject: [PATCH 69/98] Call jjtree/javacc with UTF-8 as default encoding Fixes #2970 --- pmd-core/src/main/ant/alljavacc.xml | 22 ++++++++++----- pmd-cpp/src/main/ant/alljavacc.xml | 14 +++++---- pmd-java/src/main/ant/alljavacc.xml | 24 ++++++++++------ pmd-javascript/src/main/ant/alljavacc.xml | 14 +++++---- pmd-jsp/src/main/ant/alljavacc.xml | 28 +++++++++++------- pmd-matlab/src/main/ant/alljavacc.xml | 14 +++++---- pmd-modelica/src/main/ant/alljavacc.xml | 24 ++++++++++------ pmd-objectivec/src/main/ant/alljavacc.xml | 14 +++++---- pmd-plsql/src/main/ant/alljavacc.xml | 24 ++++++++++------ pmd-python/src/main/ant/alljavacc.xml | 14 +++++---- pmd-visualforce/src/main/ant/alljavacc.xml | 33 ++++++++++++++-------- pmd-vm/src/main/ant/alljavacc.xml | 28 +++++++++++------- 12 files changed, 165 insertions(+), 88 deletions(-) diff --git a/pmd-core/src/main/ant/alljavacc.xml b/pmd-core/src/main/ant/alljavacc.xml index 2940d3ee58..ff3cb30801 100644 --- a/pmd-core/src/main/ant/alljavacc.xml +++ b/pmd-core/src/main/ant/alljavacc.xml @@ -30,15 +30,23 @@ Using JavaCC home: ${javacc-home.path} - + + + + + - + + + + + + diff --git a/pmd-cpp/src/main/ant/alljavacc.xml b/pmd-cpp/src/main/ant/alljavacc.xml index 800af4264b..c5afa6a939 100644 --- a/pmd-cpp/src/main/ant/alljavacc.xml +++ b/pmd-cpp/src/main/ant/alljavacc.xml @@ -29,11 +29,15 @@ - + + + + + + + diff --git a/pmd-java/src/main/ant/alljavacc.xml b/pmd-java/src/main/ant/alljavacc.xml index d2c6296b63..d6fcddd710 100644 --- a/pmd-java/src/main/ant/alljavacc.xml +++ b/pmd-java/src/main/ant/alljavacc.xml @@ -28,15 +28,23 @@ - + + + + + - + + + + + + + diff --git a/pmd-javascript/src/main/ant/alljavacc.xml b/pmd-javascript/src/main/ant/alljavacc.xml index 405026b5cd..fa4e9998b3 100644 --- a/pmd-javascript/src/main/ant/alljavacc.xml +++ b/pmd-javascript/src/main/ant/alljavacc.xml @@ -29,11 +29,15 @@ - + + + + + + + diff --git a/pmd-jsp/src/main/ant/alljavacc.xml b/pmd-jsp/src/main/ant/alljavacc.xml index 80c1b43001..d50b37c6f6 100644 --- a/pmd-jsp/src/main/ant/alljavacc.xml +++ b/pmd-jsp/src/main/ant/alljavacc.xml @@ -28,17 +28,25 @@ - + + + + + - + + + + + + + + + diff --git a/pmd-matlab/src/main/ant/alljavacc.xml b/pmd-matlab/src/main/ant/alljavacc.xml index 646b4740f1..41c6972b31 100644 --- a/pmd-matlab/src/main/ant/alljavacc.xml +++ b/pmd-matlab/src/main/ant/alljavacc.xml @@ -29,11 +29,15 @@ - + + + + + + + diff --git a/pmd-modelica/src/main/ant/alljavacc.xml b/pmd-modelica/src/main/ant/alljavacc.xml index 0b6c12a533..111640936c 100644 --- a/pmd-modelica/src/main/ant/alljavacc.xml +++ b/pmd-modelica/src/main/ant/alljavacc.xml @@ -28,15 +28,23 @@ - + + + + + - + + + + + + + diff --git a/pmd-objectivec/src/main/ant/alljavacc.xml b/pmd-objectivec/src/main/ant/alljavacc.xml index 16a4dd9e84..0502a8eb7f 100644 --- a/pmd-objectivec/src/main/ant/alljavacc.xml +++ b/pmd-objectivec/src/main/ant/alljavacc.xml @@ -29,11 +29,15 @@ - + + + + + + + diff --git a/pmd-plsql/src/main/ant/alljavacc.xml b/pmd-plsql/src/main/ant/alljavacc.xml index 5d0e23135a..441d04ae02 100644 --- a/pmd-plsql/src/main/ant/alljavacc.xml +++ b/pmd-plsql/src/main/ant/alljavacc.xml @@ -28,15 +28,23 @@ - + + + + + - + + + + + + + diff --git a/pmd-python/src/main/ant/alljavacc.xml b/pmd-python/src/main/ant/alljavacc.xml index 1996f86bf1..a52efb5b48 100644 --- a/pmd-python/src/main/ant/alljavacc.xml +++ b/pmd-python/src/main/ant/alljavacc.xml @@ -29,11 +29,15 @@ - + + + + + + + diff --git a/pmd-visualforce/src/main/ant/alljavacc.xml b/pmd-visualforce/src/main/ant/alljavacc.xml index fff5966898..2d4e2ad549 100644 --- a/pmd-visualforce/src/main/ant/alljavacc.xml +++ b/pmd-visualforce/src/main/ant/alljavacc.xml @@ -28,18 +28,27 @@ - - - + + + + + + + + + + + + + + + + + diff --git a/pmd-vm/src/main/ant/alljavacc.xml b/pmd-vm/src/main/ant/alljavacc.xml index 5be48f1bc5..7e4a54cb97 100644 --- a/pmd-vm/src/main/ant/alljavacc.xml +++ b/pmd-vm/src/main/ant/alljavacc.xml @@ -28,17 +28,25 @@ - + + + + + - + + + + + + + + + From ed8fbccb1cae9d140f8b1e4ee07dffc945fe7347 Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Fri, 18 Dec 2020 14:46:24 +0100 Subject: [PATCH 70/98] Add tests for additional C# 7 and C# 8 features --- .../sourceforge/pmd/cpd/CsTokenizerTest.java | 5 + .../cs/cpd/testdata/csharp7And8Additions.cs | 26 +++ .../cs/cpd/testdata/csharp7And8Additions.txt | 155 ++++++++++++++++++ 3 files changed, 186 insertions(+) create mode 100644 pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.cs create mode 100644 pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.txt diff --git a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java b/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java index 6b61c658bb..63c9cd5aee 100644 --- a/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java +++ b/pmd-cs/src/test/java/net/sourceforge/pmd/cpd/CsTokenizerTest.java @@ -100,6 +100,11 @@ public class CsTokenizerTest extends CpdTextComparisonTest { doTest("listOfNumbers", "_ignored", skipLiteralSequences()); } + @Test + public void testCSharp7And8Additions() { + doTest("csharp7And8Additions"); + } + private Properties ignoreUsings() { return properties(true, false); } diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.cs b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.cs new file mode 100644 index 0000000000..6d14774d75 --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.cs @@ -0,0 +1,26 @@ +#nullable enable +using System; +using System.Collections.Generic; + +class CSharp7And8Additions +{ + private static void Literals() + { + int x = 30_000_000; // digit separators + int b = 0b00101000; // boolean literal + } + + private static unsafe void DisplaySize() where T : unmanaged // unmanaged keyword + { + Console.WriteLine($"{typeof(T)} is unmanaged and its size is {sizeof(T)} bytes"); + } + + private static void Operators() + { + List? l = null; + (l ??= new List()).Add(5); // null-coalescing assignment operator + + var array = new int[] { 1, 2, 3, 4, 5 }; + var slice1 = array[2..^3]; // range operator + } +} diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.txt new file mode 100644 index 0000000000..5d689e8505 --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/csharp7And8Additions.txt @@ -0,0 +1,155 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [#] 1 1 +L2 + [using] 1 5 + [System] 7 12 + [;] 13 13 +L3 + [using] 1 5 + [System] 7 12 + [.] 13 13 + [Collections] 14 24 + [.] 25 25 + [Generic] 26 32 + [;] 33 33 +L5 + [class] 1 5 + [CSharp7And8Additions] 7 26 +L6 + [{] 1 1 +L7 + [private] 5 11 + [static] 13 18 + [void] 20 23 + [Literals] 25 32 + [(] 33 33 + [)] 34 34 +L8 + [{] 5 5 +L9 + [int] 9 11 + [x] 13 13 + [=] 15 15 + [30_000_000] 17 26 + [;] 27 27 +L10 + [int] 9 11 + [b] 13 13 + [=] 15 15 + [0b00101000] 17 26 + [;] 27 27 +L11 + [}] 5 5 +L13 + [private] 5 11 + [static] 13 18 + [unsafe] 20 25 + [void] 27 30 + [DisplaySize] 32 42 + [<] 43 43 + [T] 44 44 + [>] 45 45 + [(] 46 46 + [)] 47 47 + [where] 49 53 + [T] 55 55 + [:] 57 57 + [unmanaged] 59 67 +L14 + [{] 5 5 +L15 + [Console] 9 15 + [.] 16 16 + [WriteLine] 17 25 + [(] 26 26 + [$"] 27 28 + [typeof] 30 35 + [(] 36 36 + [T] 37 37 + [)] 38 38 + [ is unmanaged and its size is ] 40 69 + [sizeof] 71 76 + [(] 77 77 + [T] 78 78 + [)] 79 79 + [ bytes] 81 86 + ["] 87 87 + [)] 88 88 + [;] 89 89 +L16 + [}] 5 5 +L18 + [private] 5 11 + [static] 13 18 + [void] 20 23 + [Operators] 25 33 + [(] 34 34 + [)] 35 35 +L19 + [{] 5 5 +L20 + [List] 9 12 + [<] 13 13 + [int] 14 16 + [>] 17 17 + [?] 18 18 + [l] 20 20 + [=] 22 22 + [null] 24 27 + [;] 28 28 +L21 + [(] 9 9 + [l] 10 10 + [??=] 12 14 + [new] 16 18 + [List] 20 23 + [<] 24 24 + [int] 25 27 + [>] 28 28 + [(] 29 29 + [)] 30 30 + [)] 31 31 + [.] 32 32 + [Add] 33 35 + [(] 36 36 + [5] 37 37 + [)] 38 38 + [;] 39 39 +L23 + [var] 9 11 + [array] 13 17 + [=] 19 19 + [new] 21 23 + [int] 25 27 + [\[] 28 28 + [\]] 29 29 + [{] 31 31 + [1] 33 33 + [,] 34 34 + [2] 36 36 + [,] 37 37 + [3] 39 39 + [,] 40 40 + [4] 42 42 + [,] 43 43 + [5] 45 45 + [}] 47 47 + [;] 48 48 +L24 + [var] 9 11 + [slice1] 13 18 + [=] 20 20 + [array] 22 26 + [\[] 27 27 + [2] 28 28 + [..] 29 30 + [^] 31 31 + [3] 32 32 + [\]] 33 33 + [;] 34 34 +L25 + [}] 5 5 +L26 + [}] 1 1 +EOF From 2bbf56ec064b62389d32e8ae8389cdbd47adeea8 Mon Sep 17 00:00:00 2001 From: Ozan Gulle Date: Mon, 21 Dec 2020 21:37:09 +0100 Subject: [PATCH 71/98] Added a new test case --- .../xml/LiteralsFirstInComparisons.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml index e1d2a48dbf..28cd529172 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml @@ -354,6 +354,19 @@ public class Foo { public boolean test(String someString) { return someString.equals(TEST_CONSTANT); } +} + ]]> + + + + #575 PositionLiteralsFirstInComparisons must not trigger if the constant field is not a String + 0 + From 49fbb90cb133f6933122c885cea1d8d9d711565d Mon Sep 17 00:00:00 2001 From: Ozan Gulle Date: Mon, 21 Dec 2020 21:37:22 +0100 Subject: [PATCH 72/98] Changed naming of the method --- .../rule/bestpractices/LiteralsFirstInComparisonsRule.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java index 83200e98d6..9a036cf7b9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java @@ -136,14 +136,14 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { } private JavaNode getFirstLiteralArgument(ASTPrimarySuffix primarySuffix) { - return getArgumentPrimaryPrefixFromSuffix(primarySuffix).getFirstChildOfType(ASTLiteral.class); + return getArgumentPrimaryPrefix(primarySuffix).getFirstChildOfType(ASTLiteral.class); } private JavaNode getFirstNameArgument(ASTPrimarySuffix primarySuffix) { - return getArgumentPrimaryPrefixFromSuffix(primarySuffix).getFirstChildOfType(ASTName.class); + return getArgumentPrimaryPrefix(primarySuffix).getFirstChildOfType(ASTName.class); } - private JavaNode getArgumentPrimaryPrefixFromSuffix(ASTPrimarySuffix primarySuffix) { + private JavaNode getArgumentPrimaryPrefix(ASTPrimarySuffix primarySuffix) { ASTArguments arguments = primarySuffix.getFirstChildOfType(ASTArguments.class); ASTArgumentList argumentList = arguments.getFirstChildOfType(ASTArgumentList.class); ASTExpression expression = argumentList.getFirstChildOfType(ASTExpression.class); From 77292da7af58a3a57394ce34eaf3e70ef2cc265d Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Wed, 23 Dec 2020 10:46:34 +0100 Subject: [PATCH 73/98] Add test for empty C# comment. This was previously incorrectly tokenized, because it was recognized as the start of a documentation comment. --- .../net/sourceforge/pmd/lang/cs/cpd/testdata/comments.cs | 2 ++ .../net/sourceforge/pmd/lang/cs/cpd/testdata/comments.txt | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.cs b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.cs index 1757cf2afd..fe3d2575ce 100644 --- a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.cs +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.cs @@ -1,3 +1,5 @@ + /**/ +// the previous comment is an empty delimited comment and not a document comment class Foo { /// class X /* aaa diff --git a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.txt b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.txt index 92214cb4e9..0134960679 100644 --- a/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.txt +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/comments.txt @@ -1,8 +1,8 @@ [Image] or [Truncated image[ Bcol Ecol -L1 +L3 [class] 1 5 [Foo] 7 9 [{] 11 11 -L6 +L8 [}] 2 2 EOF From 539f457d8171db34bb5a74543b9eb913b247ea5f Mon Sep 17 00:00:00 2001 From: jr Date: Thu, 24 Dec 2020 14:21:44 -0800 Subject: [PATCH 74/98] fix: use rulechain --- .../rule/errorprone/OverrideBothEqualsAndHashcodeRule.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java index 5adf03c2b1..e9dbc37f4f 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java @@ -12,10 +12,12 @@ import net.sourceforge.pmd.lang.apex.rule.AbstractApexRule; public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule { + public OverrideBothEqualsAndHashcodeRule() { + addRuleChainVisit(ASTUserClass.class); + } + @Override public Object visit(ASTUserClass node, Object data) { - super.visit(node, data); - ApexNode equalsNode = null; ApexNode hashNode = null; for (ASTMethod method : node.findChildrenOfType(ASTMethod.class)) { From 49bab051e8d970b8967e71f61fdab56523258b2b Mon Sep 17 00:00:00 2001 From: jr Date: Thu, 24 Dec 2020 14:22:15 -0800 Subject: [PATCH 75/98] fix: use primitive --- .../rule/errorprone/OverrideBothEqualsAndHashcodeRule.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java index e9dbc37f4f..c63367e7dc 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java @@ -41,7 +41,7 @@ public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule { return data; } - private Boolean isEquals(ASTMethod node) { + private boolean isEquals(ASTMethod node) { int numParams = 0; String paramType = null; for (int ix = 0; ix < node.getNumChildren(); ix++) { @@ -54,7 +54,7 @@ public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule { return numParams == 1 && node.hasImageEqualTo("equals") && "Object".equalsIgnoreCase(paramType); } - private Boolean isHashCode(ASTMethod node) { + private boolean isHashCode(ASTMethod node) { int numParams = 0; for (int ix = 0; ix < node.getNumChildren(); ix++) { ApexNode sn = node.getChild(ix); From c5fa4cbfa0585832a5473f5e507d9e5cce691698 Mon Sep 17 00:00:00 2001 From: Vincent Maurin Date: Mon, 4 Jan 2021 15:51:27 +0100 Subject: [PATCH 76/98] Fix severity strings Code Climate issue severity are specified here https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#issues as `info`, `minor`, `major`, `critical`, or `blocker`. It was mapped to a value `normal` that is not recognized by tools compatible with Code Climate's format. I have mapped the five PMD priority values on the five Code Climate severity levels --- .../sourceforge/pmd/renderers/CodeClimateRenderer.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java index c738fac851..7430d4bb6f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/CodeClimateRenderer.java @@ -84,12 +84,16 @@ public class CodeClimateRenderer extends AbstractIncrementingRenderer { switch (rule.getPriority()) { case HIGH: - issue.severity = "critical"; + issue.severity = "blocker"; break; case MEDIUM_HIGH: + issue.severity = "critical"; + break; case MEDIUM: + issue.severity = "major"; + break; case MEDIUM_LOW: - issue.severity = "normal"; + issue.severity = "minor"; break; case LOW: default: From 59493d1e825021e556394912fe563ca4390bd0f1 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Jan 2021 12:09:10 +0100 Subject: [PATCH 77/98] [doc] Prefer github discussions for questions --- CONTRIBUTING.md | 2 +- README.md | 8 +++++--- docs/pages/pmd/about/help.md | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 372eab4e2d..0172b4acc9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,7 +45,7 @@ There are various channels, on which you can ask questions: * On [StackOverflow](https://stackoverflow.com/questions/tagged/pmd): Make sure, to tag your question with "pmd". -* Create a issue for your question at . +* Create a new discussion for your question at . * Ask your question on Gitter . diff --git a/README.md b/README.md index ca8a2941b8..f639615e8a 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,12 @@ Objective-C, Perl, PHP, PLSQL, Python, Ruby, Salesforce.com Apex, Scala, Swift, ## Support -* How do I? -- Ask a question on [StackOverflow](https://stackoverflow.com/questions/tagged/pmd). -* I got this error, why? -- Ask a question on [StackOverflow](https://stackoverflow.com/questions/tagged/pmd). +* How do I? -- Ask a question on [StackOverflow](https://stackoverflow.com/questions/tagged/pmd) + or on [discussions](https://github.com/pmd/pmd/discussions). +* I got this error, why? -- Ask a question on [StackOverflow](https://stackoverflow.com/questions/tagged/pmd) + or on [discussions](https://github.com/pmd/pmd/discussions). * I got this error and I'm sure it's a bug -- file an [issue](https://github.com/pmd/pmd/issues). -* I have an idea/request/question -- file an [issue](https://github.com/pmd/pmd/issues). +* I have an idea/request/question -- create a new [discussion](https://github.com/pmd/pmd/discussions). * I have a quick question -- ask on our [Gitter chat](https://gitter.im/pmd/pmd). * Where's your documentation? -- diff --git a/docs/pages/pmd/about/help.md b/docs/pages/pmd/about/help.md index c20f9fd066..8e45c96b8b 100644 --- a/docs/pages/pmd/about/help.md +++ b/docs/pages/pmd/about/help.md @@ -1,8 +1,8 @@ --- title: Getting Help permalink: pmd_about_help.html -author: Andreas Dangel -last_updated: September 2017 +author: Andreas Dangel +last_updated: January 2021 --- There are numerous ways of getting help: @@ -13,7 +13,7 @@ There are numerous ways of getting help: * If you found a bug, please create a new [github issue](https://github.com/pmd/pmd/issues). -* You can also ask questions in our [sourceforge forum](https://sourceforge.net/p/pmd/discussion/). +* You can also ask questions on [github discussions](https://github.com/pmd/pmd/discussions). * Or you can join the [Mailing List](https://lists.sourceforge.net/lists/listinfo/pmd-devel) or browse through the archives ([archive1](http://java-pmd.30631.n5.nabble.com/), [archive2](http://web.archive.org/web/20160715035623/http://blog.gmane.org:80/gmane.comp.java.audit.pmd.devel)). From 5ed003c6862aa14d9f6d477f924661a0a2071c79 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Jan 2021 15:28:06 +0100 Subject: [PATCH 78/98] Update gems CVE-2020-26247 https://github.com/advisories/GHSA-vr8q-g5c7-m54m --- Gemfile.lock | 20 +++++++------- docs/Gemfile.lock | 67 ++++++++++++++++++++++++++--------------------- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 9bdbcf639f..70b6a0223e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -34,18 +34,19 @@ GEM fugit (1.4.1) et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) - git (1.7.0) + git (1.8.1) rchardet (~> 1.8) kramdown (1.17.0) - liquid (4.0.3) + liquid (5.0.0) logger-colors (1.0.0) - mini_portile2 (2.4.0) + mini_portile2 (2.5.0) multipart-post (2.1.1) nap (1.1.0) no_proxy_fix (0.1.2) - nokogiri (1.10.10) - mini_portile2 (~> 2.4.0) - octokit (4.19.0) + nokogiri (1.11.1) + mini_portile2 (~> 2.5.0) + racc (~> 1.4) + octokit (4.20.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) open4 (1.3.4) @@ -58,9 +59,10 @@ GEM slop (~> 4.6) public_suffix (4.0.6) raabro (1.4.0) + racc (1.5.2) rchardet (1.8.0) - rouge (3.25.0) - rufus-scheduler (3.6.0) + rouge (3.26.0) + rufus-scheduler (3.7.0) fugit (~> 1.1, >= 1.1.6) safe_yaml (1.0.5) sawyer (0.8.2) @@ -69,7 +71,7 @@ GEM slop (4.8.2) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - tzinfo (2.0.3) + tzinfo (2.0.4) concurrent-ruby (~> 1.0) unicode-display_width (1.7.0) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 739f66ebe2..e474166447 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (6.0.3.2) + activesupport (6.0.3.4) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) @@ -17,37 +17,40 @@ GEM commonmarker (0.17.13) ruby-enum (~> 0.5) concurrent-ruby (1.1.7) - dnsruby (1.61.4) + dnsruby (1.61.5) simpleidn (~> 0.1) - em-websocket (0.5.1) + em-websocket (0.5.2) eventmachine (>= 0.12.9) http_parser.rb (~> 0.6.0) ethon (0.12.0) ffi (>= 1.3.0) eventmachine (1.2.7) execjs (2.7.0) - faraday (1.0.1) + faraday (1.3.0) + faraday-net_http (~> 1.0) multipart-post (>= 1.2, < 3) - ffi (1.13.1) + ruby2_keywords + faraday-net_http (1.0.0) + ffi (1.14.2) forwardable-extended (2.6.0) gemoji (3.0.1) - github-pages (207) + github-pages (209) github-pages-health-check (= 1.16.1) jekyll (= 3.9.0) jekyll-avatar (= 0.7.0) jekyll-coffeescript (= 1.1.1) jekyll-commonmark-ghpages (= 0.1.6) jekyll-default-layout (= 0.1.4) - jekyll-feed (= 0.13.0) + jekyll-feed (= 0.15.1) jekyll-gist (= 1.5.0) jekyll-github-metadata (= 2.13.0) - jekyll-mentions (= 1.5.1) + jekyll-mentions (= 1.6.0) jekyll-optional-front-matter (= 0.3.2) jekyll-paginate (= 1.1.0) jekyll-readme-index (= 0.3.0) - jekyll-redirect-from (= 0.15.0) + jekyll-redirect-from (= 0.16.0) jekyll-relative-links (= 0.6.1) - jekyll-remote-theme (= 0.4.1) + jekyll-remote-theme (= 0.4.2) jekyll-sass-converter (= 1.5.2) jekyll-seo-tag (= 2.6.1) jekyll-sitemap (= 1.4.0) @@ -55,7 +58,7 @@ GEM jekyll-theme-architect (= 0.1.1) jekyll-theme-cayman (= 0.1.1) jekyll-theme-dinky (= 0.1.1) - jekyll-theme-hacker (= 0.1.1) + jekyll-theme-hacker (= 0.1.2) jekyll-theme-leap-day (= 0.1.1) jekyll-theme-merlot (= 0.1.1) jekyll-theme-midnight (= 0.1.1) @@ -66,14 +69,14 @@ GEM jekyll-theme-tactile (= 0.1.1) jekyll-theme-time-machine (= 0.1.1) jekyll-titles-from-headings (= 0.5.3) - jemoji (= 0.11.1) + jemoji (= 0.12.0) kramdown (= 2.3.0) kramdown-parser-gfm (= 1.1.0) liquid (= 4.0.3) mercenary (~> 0.3) minima (= 2.5.1) nokogiri (>= 1.10.4, < 2.0) - rouge (= 3.19.0) + rouge (= 3.23.0) terminal-table (~> 1.4) github-pages-health-check (1.16.1) addressable (~> 2.3) @@ -114,14 +117,14 @@ GEM rouge (>= 2.0, < 4.0) jekyll-default-layout (0.1.4) jekyll (~> 3.0) - jekyll-feed (0.13.0) + jekyll-feed (0.15.1) jekyll (>= 3.7, < 5.0) jekyll-gist (1.5.0) octokit (~> 4.2) jekyll-github-metadata (2.13.0) jekyll (>= 3.4, < 5.0) octokit (~> 4.0, != 4.4.0) - jekyll-mentions (1.5.1) + jekyll-mentions (1.6.0) html-pipeline (~> 2.3) jekyll (>= 3.7, < 5.0) jekyll-optional-front-matter (0.3.2) @@ -129,14 +132,15 @@ GEM jekyll-paginate (1.1.0) jekyll-readme-index (0.3.0) jekyll (>= 3.0, < 5.0) - jekyll-redirect-from (0.15.0) + jekyll-redirect-from (0.16.0) jekyll (>= 3.3, < 5.0) jekyll-relative-links (0.6.1) jekyll (>= 3.3, < 5.0) - jekyll-remote-theme (0.4.1) + jekyll-remote-theme (0.4.2) addressable (~> 2.0) jekyll (>= 3.5, < 5.0) - rubyzip (>= 1.3.0) + jekyll-sass-converter (>= 1.0, <= 3.0.0, != 2.0.0) + rubyzip (>= 1.3.0, < 3.0) jekyll-sass-converter (1.5.2) sass (~> 3.4) jekyll-seo-tag (2.6.1) @@ -153,8 +157,8 @@ GEM jekyll-theme-dinky (0.1.1) jekyll (~> 3.5) jekyll-seo-tag (~> 2.0) - jekyll-theme-hacker (0.1.1) - jekyll (~> 3.5) + jekyll-theme-hacker (0.1.2) + jekyll (> 3.5, < 5.0) jekyll-seo-tag (~> 2.0) jekyll-theme-leap-day (0.1.1) jekyll (~> 3.5) @@ -188,7 +192,7 @@ GEM jekyll (>= 3.3, < 5.0) jekyll-watch (2.2.1) listen (~> 3.0) - jemoji (0.11.1) + jemoji (0.12.0) gemoji (~> 3.0) html-pipeline (~> 2.2) jekyll (>= 3.0, < 5.0) @@ -197,32 +201,35 @@ GEM kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) liquid (4.0.3) - listen (3.2.1) + listen (3.4.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) mercenary (0.3.6) - mini_portile2 (2.4.0) + mini_portile2 (2.5.0) minima (2.5.1) jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.14.1) + minitest (5.14.3) multipart-post (2.1.1) - nokogiri (1.10.10) - mini_portile2 (~> 2.4.0) - octokit (4.18.0) + nokogiri (1.11.1) + mini_portile2 (~> 2.5.0) + racc (~> 1.4) + octokit (4.20.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (3.1.1) + racc (1.5.2) rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) rexml (3.2.4) - rouge (3.19.0) + rouge (3.23.0) ruby-enum (0.8.0) i18n + ruby2_keywords (0.0.2) rubyzip (2.3.0) safe_yaml (1.0.5) sass (3.7.4) @@ -240,13 +247,13 @@ GEM thread_safe (0.3.6) typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (1.2.7) + tzinfo (1.2.9) thread_safe (~> 0.1) unf (0.1.4) unf_ext unf_ext (0.0.7.7) unicode-display_width (1.7.0) - zeitwerk (2.4.0) + zeitwerk (2.4.2) PLATFORMS ruby From 4b99f42657babdc7b9e248c839d2827d9aac8dca Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Jan 2021 15:31:45 +0100 Subject: [PATCH 79/98] [doc] Fix link to deprecated RulesetsFactoryUtils --- docs/pages/next_major_development.md | 2 +- docs/pages/release_notes_old.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/next_major_development.md b/docs/pages/next_major_development.md index d8b0d6a550..94d7546cbd 100644 --- a/docs/pages/next_major_development.md +++ b/docs/pages/next_major_development.md @@ -131,7 +131,7 @@ we may decide to remove some APIs that were not tagged as deprecated, though we' ###### Around RuleSet parsing -* {% jdoc core::RuleSetFactory %} and {% jdoc core::RuleSetFactoryUtils %} have been deprecated in favor of {% jdoc core::RuleSetLoader %}. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. +* {% jdoc core::RuleSetFactory %} and {% jdoc core::RulesetsFactoryUtils %} have been deprecated in favor of {% jdoc core::RuleSetLoader %}. This is easier to configure, and more maintainable than the multiple overloads of `RulesetsFactoryUtils`. * Some static creation methods have been added to {% jdoc core::RuleSet %} for simple cases, eg {% jdoc core::RuleSet#forSingleRule(core::Rule) %}. These replace some counterparts in {% jdoc core::RuleSetFactory %} * Since {% jdoc core::RuleSets %} is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. * {% jdoc core::RuleSetReferenceId %}, {% jdoc core::RuleSetReference %}, {% jdoc core::RuleSetFactoryCompatibility %} are deprecated. They are most likely not relevant outside of the implementation of pmd-core. diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index cc2bb4a7f7..85489a2b5e 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -70,7 +70,7 @@ Thanks to Jeff Bartolotta and Roopa Mohan for contributing this! ##### Around RuleSet parsing -* RuleSetFactory and RulesetsFactoryUtils have been deprecated in favor of RuleSetLoader. This is easier to configure, and more maintainable than the multiple overloads of `RuleSetFactoryUtils`. +* RuleSetFactory and RulesetsFactoryUtils have been deprecated in favor of RuleSetLoader. This is easier to configure, and more maintainable than the multiple overloads of `RulesetsFactoryUtils`. * Some static creation methods have been added to RuleSet for simple cases, eg forSingleRule. These replace some counterparts in RuleSetFactory * Since RuleSets is also deprecated, many APIs that require a RuleSets instance now are deprecated, and have a counterpart that expects a `List`. * RuleSetReferenceId, RuleSetReference, RuleSetFactoryCompatibility are deprecated. They are most likely not relevant outside of the implementation of pmd-core. From 740d832dc1af9485b35827c3e3f29e86161dcd83 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Jan 2021 19:51:23 +0100 Subject: [PATCH 80/98] Change from PR review #2983 --- .../bestpractices/LiteralsFirstInComparisonsRule.java | 2 +- .../rule/bestpractices/xml/LiteralsFirstInComparisons.xml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java index 9a036cf7b9..d89bcedf5e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsRule.java @@ -168,7 +168,7 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { for (ASTFieldDeclaration fieldDeclaration : fieldDeclarations) { ASTVariableDeclarator declaration = fieldDeclaration.getFirstChildOfType(ASTVariableDeclarator.class); if (declaration.getName().equals(name.getImage()) - && "class java.lang.String".equals(declaration.getType().toString()) + && String.class.equals(declaration.getType()) && fieldDeclaration.isFinal() && fieldDeclaration.isStatic()) { return true; diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml index 28cd529172..903bb45279 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/LiteralsFirstInComparisons.xml @@ -320,7 +320,7 @@ public class Foo { - #575 PositionLiteralsFirstInComparisons to consider constant fields, i.e. static final Strings + #575 LiteralsFirstInComparisons to consider constant fields, i.e. static final Strings 1 - #575 PositionLiteralsFirstInComparisons must not trigger if the field is not final + #575 LiteralsFirstInComparisons must not trigger if the field is not final 0 - #575 PositionLiteralsFirstInComparisons must not trigger if the field is not static + #575 LiteralsFirstInComparisons must not trigger if the field is not static 0 - #575 PositionLiteralsFirstInComparisons must not trigger if the constant field is not a String + #575 LiteralsFirstInComparisons must not trigger if the constant field is not a String 0 Date: Thu, 7 Jan 2021 19:54:51 +0100 Subject: [PATCH 81/98] [doc] Update release notes, refs #2983, fixes #575 --- docs/pages/release_notes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b8f8783555..89cb352488 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -16,9 +16,14 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues +* java-bestpractices + * [#575](https://github.com/pmd/pmd/issues/575): \[java] LiteralsFirstInComparisons should consider constant fields + ### API Changes ### External Contributions +* [#2983](https://github.com/pmd/pmd/pull/2983): \[java] LiteralsFirstInComparisons should consider constant fields - [Ozan Gulle](https://github.com/ozangulle) + {% endtocmaker %} From c7cd242ab2465bdcd6a1b0397c0dcd687c5a15ea Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Jan 2021 20:14:29 +0100 Subject: [PATCH 82/98] [core] Renderers - adjust tests for different rule priorities (#2994) --- .../sourceforge/pmd/renderers/AbstractRendererTest.java | 9 +++++++-- .../net/sourceforge/pmd/renderers/CSVRendererTest.java | 2 +- .../pmd/renderers/CodeClimateRendererTest.java | 4 ++-- .../net/sourceforge/pmd/renderers/JsonRendererTest.java | 1 + .../net/sourceforge/pmd/renderers/XMLRendererTest.java | 2 +- .../pmd/renderers/json/expected-multiple.json | 2 +- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java index 97e0e6664d..bff13f27ef 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java @@ -16,6 +16,7 @@ import net.sourceforge.pmd.Report.ConfigurationError; import net.sourceforge.pmd.Report.ProcessingError; import net.sourceforge.pmd.ReportTest; import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleViolation; import net.sourceforge.pmd.RuleWithProperties; import net.sourceforge.pmd.lang.ast.DummyNode; @@ -69,8 +70,12 @@ public abstract class AbstractRendererTest { private Report reportTwoViolations() { Report report = new Report(); - report.addRuleViolation(newRuleViolation(1)); - report.addRuleViolation(newRuleViolation(2)); + RuleViolation informationalRuleViolation = newRuleViolation(1); + informationalRuleViolation.getRule().setPriority(RulePriority.LOW); + report.addRuleViolation(informationalRuleViolation); + RuleViolation severeRuleViolation = newRuleViolation(2); + severeRuleViolation.getRule().setPriority(RulePriority.HIGH); + report.addRuleViolation(severeRuleViolation); return report; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CSVRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CSVRendererTest.java index 029dd303d8..5ef24218fe 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CSVRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CSVRendererTest.java @@ -30,7 +30,7 @@ public class CSVRendererTest extends AbstractRendererTest { public String getExpectedMultiple() { return getHeader() + "\"1\",\"\",\"" + getSourceCodeFilename() + "\",\"5\",\"1\",\"blah\",\"RuleSet\",\"Foo\"" + PMD.EOL - + "\"2\",\"\",\"" + getSourceCodeFilename() + "\",\"5\",\"1\",\"blah\",\"RuleSet\",\"Foo\"" + PMD.EOL; + + "\"2\",\"\",\"" + getSourceCodeFilename() + "\",\"1\",\"1\",\"blah\",\"RuleSet\",\"Foo\"" + PMD.EOL; } @Override diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java index 089c8d12a2..ca24c5a909 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/CodeClimateRendererTest.java @@ -76,7 +76,7 @@ public class CodeClimateRendererTest extends AbstractRendererTest { + "violationSuppressXPath | | Suppress violations on nodes which match a given relative XPath expression.\\n" + "\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"" + getSourceCodeFilename() + "\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}" + "\u0000" + PMD.EOL + "{\"type\":\"issue\",\"check_name\":\"Foo\",\"description\":\"blah\"," - + "\"content\":{\"body\":\"## Foo\\n\\nSince: PMD null\\n\\nPriority: Low\\n\\n" + + "\"content\":{\"body\":\"## Foo\\n\\nSince: PMD null\\n\\nPriority: High\\n\\n" + "[Categories](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#categories): Style\\n\\n" + "[Remediation Points](https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#remediation-points): 50000\\n\\n" + "desc\\n\\n" @@ -84,7 +84,7 @@ public class CodeClimateRendererTest extends AbstractRendererTest { + "Name | Value | Description\\n" + "--- | --- | ---\\n" + "violationSuppressRegex | | Suppress violations with messages matching a regular expression\\n" + "violationSuppressXPath | | Suppress violations on nodes which match a given relative XPath expression.\\n" - + "\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"" + getSourceCodeFilename() + "\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"info\",\"remediation_points\":50000}" + + "\"},\"categories\":[\"Style\"],\"location\":{\"path\":\"" + getSourceCodeFilename() + "\",\"lines\":{\"begin\":1,\"end\":1}},\"severity\":\"blocker\",\"remediation_points\":50000}" + "\u0000" + PMD.EOL; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/JsonRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/JsonRendererTest.java index 2a1f08fd3c..524277fe21 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/JsonRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/JsonRendererTest.java @@ -79,6 +79,7 @@ public class JsonRendererTest extends AbstractRendererTest { public String filter(String expected) { String result = expected .replaceAll("\"timestamp\":\\s*\"[^\"]+\"", "\"timestamp\": \"--replaced--\"") + .replaceAll("\"pmdVersion\":\\s*\"[^\"]+\"", "\"pmdVersion\": \"unknown\"") .replaceAll("\r\n", "\n"); // make the test run on Windows, too return result; } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java index 62e628c106..6e11717f08 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/XMLRendererTest.java @@ -63,7 +63,7 @@ public class XMLRendererTest extends AbstractRendererTest { return getHeader() + "" + PMD.EOL + "" + PMD.EOL + "blah" + PMD.EOL + "" + PMD.EOL - + "" + + "" + PMD.EOL + "blah" + PMD.EOL + "" + PMD.EOL + "" + PMD.EOL + "" + PMD.EOL; } diff --git a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/json/expected-multiple.json b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/json/expected-multiple.json index 15717aed32..9ab3704f1e 100644 --- a/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/json/expected-multiple.json +++ b/pmd-core/src/test/resources/net/sourceforge/pmd/renderers/json/expected-multiple.json @@ -24,7 +24,7 @@ "description": "blah", "rule": "Foo", "ruleset": "RuleSet", - "priority": 5 + "priority": 1 } ] } From a5a51f0d5e35c7014d373dd5f7fbb7ec2f7bfeaa Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 7 Jan 2021 20:16:40 +0100 Subject: [PATCH 83/98] [doc] Update release notes, refs #2994 --- docs/pages/release_notes.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b8f8783555..efd1ca7f67 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -16,9 +16,14 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues +* core + * [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings + ### API Changes ### External Contributions +* [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings - [Vincent Maurin](https://github.com/vmaurin) + {% endtocmaker %} From 49ff9a5c5ffe57d2884a089ddec1d939fa530537 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 8 Jan 2021 15:33:48 +0100 Subject: [PATCH 84/98] [ci] Increase build timeout for pull requests to 60 minutes --- .github/workflows/pull-requests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index 169f000a80..9fd0c2fcbe 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -6,7 +6,7 @@ jobs: build: runs-on: ${{ matrix.os }} continue-on-error: false - timeout-minutes: 30 + timeout-minutes: 60 strategy: matrix: os: [ ubuntu-latest, windows-latest, macos-latest ] From fde4d08213cac5e4fefa063fec0e5987d7bc58e6 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 8 Jan 2021 15:38:47 +0100 Subject: [PATCH 85/98] [ci] Fail if baseline couldn't be created/uploaded --- .ci/inc/regression-tester.inc | 63 +++++++++++------------------------ 1 file changed, 20 insertions(+), 43 deletions(-) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index e959e9a780..7f5298abb4 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -33,54 +33,31 @@ function regression_tester_setup_ci() { } # -# Generate a new baseline and upload it to sourceforge -# -# Note: this function always succeeds, even if the upload fails. -# In that case, just a error logging is provided. +# Generate a new baseline and upload it to pmd-code.org # function regression_tester_uploadBaseline() { log_debug "$FUNCNAME branch=${PMD_CI_BRANCH}" - local targetUrl="https://sourceforge.net/projects/pmd/files/pmd-regression-tester/" local pmdcodeUrl="https://pmd-code.org/pmd-regression-tester/" - local errexitstate="$(shopt -po errexit)" - set +e # disable errexit - ( - # This handler is called if any command fails - function upload_failed() { - log_error "Error while uploading ${BRANCH_FILENAME}-baseline.zip to pmd-code.org!" - log_error "Please upload manually: ${pmdcodeUrl}" - #log_error "Error while uploading ${BRANCH_FILENAME}-baseline.zip to sourceforge!" - #log_error "Please upload manually: ${targetUrl}" - } - - # exit subshell after trap - set -e - trap upload_failed ERR - - log_info "Generating and uploading baseline for pmdtester..." - cd .. - bundle config --local gemfile pmd/Gemfile - bundle config set --local path pmd/vendor/bundle - bundle exec pmdtester \ - --mode single \ - --local-git-repo ./pmd \ - --patch-branch ${PMD_CI_BRANCH:-$PMD_CI_TAG} \ - --patch-config ./pmd/.ci/files/all-java.xml \ - --list-of-project ./pmd/.ci/files/project-list.xml --html-flag \ - --error-recovery - cd target/reports - BRANCH_FILENAME="${PMD_CI_BRANCH:-$PMD_CI_TAG}" - BRANCH_FILENAME="${BRANCH_FILENAME/\//_}" - zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ - # ssh-key for pmd-code.org is setup already by pmd_ci_setup_ssh - scp ${BRANCH_FILENAME}-baseline.zip pmd@pmd-code.org:/httpdocs/pmd-regression-tester/ - log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to ${pmdcodeUrl}" - #../../pmd/.ci/travis_wait "rsync -avh ${BRANCH_FILENAME}-baseline.zip ${PMD_SF_USER}@web.sourceforge.net:/home/frs/project/pmd/pmd-regression-tester/" - #log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to ${targetUrl}" - ) - # restore errexit state - eval "$errexitstate" + log_info "Generating and uploading baseline for pmdtester..." + pushd .. + bundle config --local gemfile pmd/Gemfile + bundle config set --local path pmd/vendor/bundle + bundle exec pmdtester \ + --mode single \ + --local-git-repo ./pmd \ + --patch-branch ${PMD_CI_BRANCH:-$PMD_CI_TAG} \ + --patch-config ./pmd/.ci/files/all-java.xml \ + --list-of-project ./pmd/.ci/files/project-list.xml --html-flag \ + --error-recovery + cd target/reports + BRANCH_FILENAME="${PMD_CI_BRANCH:-$PMD_CI_TAG}" + BRANCH_FILENAME="${BRANCH_FILENAME/\//_}" + zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ + # ssh-key for pmd-code.org is setup already by pmd_ci_setup_ssh + scp ${BRANCH_FILENAME}-baseline.zip pmd@pmd-code.org:/httpdocs/pmd-regression-tester/ + log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to ${pmdcodeUrl}" + popd } # From 1ae70c2fa069a7a4875d92988a35df98b677d7c2 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 8 Jan 2021 15:50:42 +0100 Subject: [PATCH 86/98] [ci] Fix config for bundler for executing pmdtester --- .ci/inc/regression-tester.inc | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index 7f5298abb4..29ec49adea 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -36,28 +36,29 @@ function regression_tester_setup_ci() { # Generate a new baseline and upload it to pmd-code.org # function regression_tester_uploadBaseline() { - log_debug "$FUNCNAME branch=${PMD_CI_BRANCH}" local pmdcodeUrl="https://pmd-code.org/pmd-regression-tester/" + local baseline_branch="${PMD_CI_BRANCH:-$PMD_CI_TAG}" + log_debug "$FUNCNAME branch=${baseline_branch}" - log_info "Generating and uploading baseline for pmdtester..." + log_info "Generating and uploading baseline for pmdtester (${baseline_branch})..." pushd .. - bundle config --local gemfile pmd/Gemfile - bundle config set --local path pmd/vendor/bundle + rm -f .bundle/config + bundle config set --local gemfile pmd/Gemfile bundle exec pmdtester \ --mode single \ --local-git-repo ./pmd \ - --patch-branch ${PMD_CI_BRANCH:-$PMD_CI_TAG} \ + --patch-branch ${baseline_branch} \ --patch-config ./pmd/.ci/files/all-java.xml \ --list-of-project ./pmd/.ci/files/project-list.xml --html-flag \ --error-recovery - cd target/reports - BRANCH_FILENAME="${PMD_CI_BRANCH:-$PMD_CI_TAG}" - BRANCH_FILENAME="${BRANCH_FILENAME/\//_}" + pushd target/reports + BRANCH_FILENAME="${baseline_branch/\//_}" zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ # ssh-key for pmd-code.org is setup already by pmd_ci_setup_ssh scp ${BRANCH_FILENAME}-baseline.zip pmd@pmd-code.org:/httpdocs/pmd-regression-tester/ log_success "Successfully uploaded ${BRANCH_FILENAME}-baseline.zip to ${pmdcodeUrl}" popd + popd } # From ddcc2055166276c8682ad48f751b0eefb4131a70 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 8 Jan 2021 15:59:33 +0100 Subject: [PATCH 87/98] [ci] Fail if danger couldn't execute pmdtester or upload result --- .ci/inc/regression-tester.inc | 50 +++++++++++------------------------ Dangerfile | 2 +- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index 29ec49adea..3bc72ccd81 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -64,42 +64,24 @@ function regression_tester_uploadBaseline() { # # Execute danger, which executes pmd-regression-tester (via Dangerfile). # -# Note: this function always succeeds, even if the danger fails. -# In that case, just a error logging is provided. -# function regression_tester_executeDanger() { log_debug "$FUNCNAME" - local errexitstate="$(shopt -po errexit)" - set +e # disable errexit - ( - # This handler is called if any command fails - function danger_failed() { - log_error "Error while executing danger/pmd-regression-tester" - } + # Create a corresponding remote branch locally + if ! git show-ref --verify --quiet refs/heads/${PMD_CI_BRANCH}; then + git fetch --no-tags --depth=1 origin +refs/heads/${PMD_CI_BRANCH}:refs/remotes/origin/${PMD_CI_BRANCH} + git branch ${PMD_CI_BRANCH} origin/${PMD_CI_BRANCH} + log_debug "Created local branch ${PMD_CI_BRANCH}" + fi + # Fetch more commits of the PR for danger and regression tester + git fetch --no-tags --depth=50 origin +$(git rev-parse HEAD^2): + # Fetch more commits from master branch for regression tester + if [[ "${PMD_CI_BRANCH}" != "master" ]]; then + git fetch --no-tags --depth=50 origin +master: + git branch master origin/master + fi - # exit subshell after trap - set -e - trap danger_failed ERR - - # Create a corresponding remote branch locally - if ! git show-ref --verify --quiet refs/heads/${PMD_CI_BRANCH}; then - git fetch --no-tags --depth=1 origin +refs/heads/${PMD_CI_BRANCH}:refs/remotes/origin/${PMD_CI_BRANCH} - git branch ${PMD_CI_BRANCH} origin/${PMD_CI_BRANCH} - log_debug "Created local branch ${PMD_CI_BRANCH}" - fi - # Fetch more commits of the PR for danger and regression tester - git fetch --no-tags --depth=50 origin +$(git rev-parse HEAD^2): - # Fetch more commits from master branch for regression tester - if [[ "${PMD_CI_BRANCH}" != "master" ]]; then - git fetch --no-tags --depth=50 origin +master: - git branch master origin/master - fi - - log_info "Running danger on branch ${PMD_CI_BRANCH}" - bundle exec danger --verbose - log_success "Executing danger successfully" - ) - # restore errexit state - eval "$errexitstate" + log_info "Running danger on branch ${PMD_CI_BRANCH}" + bundle exec danger --verbose + log_success "Executed danger successfully" } diff --git a/Dangerfile b/Dangerfile index 07cc07a715..d43916e4c7 100644 --- a/Dangerfile +++ b/Dangerfile @@ -39,7 +39,7 @@ def upload_report `tar -cf #{tar_filename} diff/` report_url = `curl -u #{ENV['PMD_CI_CHUNK_TOKEN']} -T #{tar_filename} https://chunk.io` if $?.success? - @logger.info "Successfully uploaded #{tar_filename} to chunk.io" + @logger.info "Successfully uploaded #{tar_filename} to #{report_url}" # set value of sticky to true and the message is kept after new commits are submitted to the PR message("This changeset " \ From 83bc024fd367591c40b60f68e1d0344f31e35962 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 8 Jan 2021 16:09:22 +0100 Subject: [PATCH 88/98] [ci] Enable debug mode for pmdtester --- .ci/inc/regression-tester.inc | 3 ++- Dangerfile | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index 3bc72ccd81..28575083d9 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -50,7 +50,8 @@ function regression_tester_uploadBaseline() { --patch-branch ${baseline_branch} \ --patch-config ./pmd/.ci/files/all-java.xml \ --list-of-project ./pmd/.ci/files/project-list.xml --html-flag \ - --error-recovery + --error-recovery \ + --debug pushd target/reports BRANCH_FILENAME="${baseline_branch/\//_}" zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ diff --git a/Dangerfile b/Dangerfile index d43916e4c7..ae23da1367 100644 --- a/Dangerfile +++ b/Dangerfile @@ -16,7 +16,7 @@ def run_pmdtester '--auto-gen-config', '--error-recovery', '--baseline-download-url', 'https://pmd-code.org/pmd-regression-tester/', - # '--debug', + '--debug', ] begin @summary = PmdTester::Runner.new(argv).run From 93e567b8ea7f504aa0b5499bc2b157d5757b1dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 8 Jan 2021 21:39:33 +0100 Subject: [PATCH 89/98] Update release notes, refs #2964 --- docs/pages/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b8f8783555..f6e1ff6b89 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -20,5 +20,7 @@ This is a {{ site.pmd.release_type }} release. ### External Contributions +- [#2964](https://github.com/pmd/pmd/pull/2964) \[cs] Update C# grammar for additional C# 7 and C# 8 features -- [Maikel Steneker](https://github.com/maikelsteneker) + {% endtocmaker %} From e8a0ba9815f3e6a1928ed671388847778506603c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Jan 2021 18:22:33 +0100 Subject: [PATCH 90/98] Make map not static in MethodNamingConventionsRule Fix #2960 --- .../java/rule/codestyle/MethodNamingConventionsRule.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java index 46c74a8568..0183e2889c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsRule.java @@ -22,7 +22,7 @@ import net.sourceforge.pmd.properties.PropertyDescriptor; public class MethodNamingConventionsRule extends AbstractNamingConventionRule { - private static final Map DESCRIPTOR_TO_DISPLAY_NAME = new HashMap<>(); + private final Map descriptorToDisplayName = new HashMap<>(); @Deprecated private static final BooleanProperty CHECK_NATIVE_METHODS_DESCRIPTOR = new BooleanProperty("checkNativeMethods", @@ -118,7 +118,7 @@ public class MethodNamingConventionsRule extends AbstractNamingConventionRule descriptor) { - return DESCRIPTOR_TO_DISPLAY_NAME.get(descriptor.name()); + return descriptorToDisplayName.get(descriptor.name()); } } From ec4dcdd262eae34722c2068856376cae2390e612 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 15 Jan 2021 18:24:27 +0100 Subject: [PATCH 91/98] [doc] Update release notes, refs #2970 --- docs/pages/release_notes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index b8f8783555..468a8a0ecc 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -16,6 +16,9 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues +* core + * [#2970](https://github.com/pmd/pmd/issues/2970): \[core] PMD 6.30.0 release is not reproducible + ### API Changes ### External Contributions From 8f021502de2febd9281aa7acbc905d5708acdfdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 15 Jan 2021 18:25:28 +0100 Subject: [PATCH 92/98] Update release notes Refs #3060 --- docs/pages/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 9d2add0d98..9847ce0625 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -20,6 +20,8 @@ This is a {{ site.pmd.release_type }} release. * [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings * java-bestpractices * [#575](https://github.com/pmd/pmd/issues/575): \[java] LiteralsFirstInComparisons should consider constant fields +* java-codestyle + * [#2960](https://github.com/pmd/pmd/issues/2960): \[java] Thread issue in MethodNamingConventionsRule ### API Changes From 9158c9f439a6c6a1dfad4020fff78c73a060caa7 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 16 Jan 2021 10:11:22 +0100 Subject: [PATCH 93/98] [ci] Update pmdtester to 1.1.1, disable debug --- .ci/inc/regression-tester.inc | 3 +-- Dangerfile | 2 +- Gemfile | 2 +- Gemfile.lock | 8 ++++---- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index 28575083d9..3bc72ccd81 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -50,8 +50,7 @@ function regression_tester_uploadBaseline() { --patch-branch ${baseline_branch} \ --patch-config ./pmd/.ci/files/all-java.xml \ --list-of-project ./pmd/.ci/files/project-list.xml --html-flag \ - --error-recovery \ - --debug + --error-recovery pushd target/reports BRANCH_FILENAME="${baseline_branch/\//_}" zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ diff --git a/Dangerfile b/Dangerfile index ae23da1367..1d51856dab 100644 --- a/Dangerfile +++ b/Dangerfile @@ -16,7 +16,7 @@ def run_pmdtester '--auto-gen-config', '--error-recovery', '--baseline-download-url', 'https://pmd-code.org/pmd-regression-tester/', - '--debug', + #'--debug', ] begin @summary = PmdTester::Runner.new(argv).run diff --git a/Gemfile b/Gemfile index b8861eb9c3..67b22b628d 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,7 @@ source 'https://rubygems.org/' # bleeding edge from git #gem 'pmdtester', :git => 'https://github.com/pmd/pmd-regression-tester.git' -gem 'pmdtester', '~> 1.1' +gem 'pmdtester', '~> 1' gem 'danger', '~> 5.6', '>= 5.6' # This group is only needed for rendering release notes diff --git a/Gemfile.lock b/Gemfile.lock index 70b6a0223e..e742be01b1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -31,7 +31,7 @@ GEM multipart-post (>= 1.2, < 3) faraday-http-cache (1.3.1) faraday (~> 0.8) - fugit (1.4.1) + fugit (1.4.2) et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) git (1.8.1) @@ -50,11 +50,11 @@ GEM faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) open4 (1.3.4) - pmdtester (1.1.0) + pmdtester (1.1.1) differ (~> 0.1) liquid (>= 4.0) logger-colors (~> 1.0) - nokogiri (~> 1.8) + nokogiri (>= 1.11.0.rc4) rufus-scheduler (~> 3.5) slop (~> 4.6) public_suffix (4.0.6) @@ -81,7 +81,7 @@ PLATFORMS DEPENDENCIES danger (~> 5.6, >= 5.6) liquid (>= 4.0.0) - pmdtester (~> 1.1) + pmdtester (~> 1) rouge (>= 1.7, < 4) safe_yaml (>= 1.0) From ddec921e38c6d67be2c3fbdf03ab29e7e9d0e381 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sun, 17 Jan 2021 18:31:33 +0100 Subject: [PATCH 94/98] [apex] Fixes from PR #2959 - Update description of new rule OverrideBothEqualsAndHashcode - add to quickstart (commented out) - use case insensitive comparison --- .../OverrideBothEqualsAndHashcodeRule.java | 6 +- .../resources/category/apex/errorprone.xml | 99 ++++++++++--------- .../resources/rulesets/apex/quickstart.xml | 1 + .../xml/OverrideBothEqualsAndHashcode.xml | 6 +- 4 files changed, 59 insertions(+), 53 deletions(-) diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java index c63367e7dc..29d90ad523 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/errorprone/OverrideBothEqualsAndHashcodeRule.java @@ -1,4 +1,4 @@ -/** +/* * BSD-style license; for more info see http://pmd.sourceforge.net/license.html */ @@ -51,7 +51,7 @@ public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule { paramType = ((ASTParameter) sn).getType(); } } - return numParams == 1 && node.hasImageEqualTo("equals") && "Object".equalsIgnoreCase(paramType); + return numParams == 1 && "equals".equalsIgnoreCase(node.getImage()) && "Object".equalsIgnoreCase(paramType); } private boolean isHashCode(ASTMethod node) { @@ -63,6 +63,6 @@ public class OverrideBothEqualsAndHashcodeRule extends AbstractApexRule { } } - return numParams == 0 && node.hasImageEqualTo("hashCode"); + return numParams == 0 && "hashCode".equalsIgnoreCase(node.getImage()); } } diff --git a/pmd-apex/src/main/resources/category/apex/errorprone.xml b/pmd-apex/src/main/resources/category/apex/errorprone.xml index a9d56f5d04..66779d65b6 100644 --- a/pmd-apex/src/main/resources/category/apex/errorprone.xml +++ b/pmd-apex/src/main/resources/category/apex/errorprone.xml @@ -111,6 +111,30 @@ public without sharing class Foo { + + + Apex supported non existent annotations for legacy reasons. + In the future, use of such non-existent annotations could result in broken apex code that will not compile. + This will prevent users of garbage annotations from being able to use legitimate annotations added to Apex in the future. + A full list of supported annotations can be found at https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation.htm + + 3 + + + + + - - - - - -Override both public boolean Object.equals(Object other), and public int Object.hashCode(), or override neither. Even if you are inheriting a hashCode() from a parent class, consider implementing hashCode and explicitly delegating to your superclass. - - 3 - - @@ -351,24 +341,39 @@ public class MyClass { - + since="6.31.0" + message="Ensure you override both equals() and hashCode()" + class="net.sourceforge.pmd.lang.apex.rule.errorprone.OverrideBothEqualsAndHashcodeRule" + externalInfoUrl="${pmd.website.baseurl}/pmd_rules_apex_errorprone.html#overridebothequalsandhashcode"> - Apex supported non existent annotations for legacy reasons. - In the future, use of such non-existent annotations could result in broken apex code that will not compile. - This will prevent users of garbage annotations from being able to use legitimate annotations added to Apex in the future. - A full list of supported annotations can be found at https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation.htm +Override both `public Boolean equals(Object obj)`, and `public Integer hashCode()`, or override neither. +Even if you are inheriting a hashCode() from a parent class, consider implementing hashCode and explicitly +delegating to your superclass. + +This is especially important when [Using Custom Types in Map Keys and Sets](https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_collections_maps_keys_userdefined.htm). 3 - diff --git a/pmd-apex/src/main/resources/rulesets/apex/quickstart.xml b/pmd-apex/src/main/resources/rulesets/apex/quickstart.xml index 10b0ddcfcc..c0842d8641 100644 --- a/pmd-apex/src/main/resources/rulesets/apex/quickstart.xml +++ b/pmd-apex/src/main/resources/rulesets/apex/quickstart.xml @@ -472,5 +472,6 @@ + diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml index c5305e28e9..d321c92777 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/errorprone/xml/OverrideBothEqualsAndHashcode.xml @@ -37,12 +37,12 @@ public class Foo { - nested equals only + nested equals only, checking case insensitiveness 1 @@ -106,7 +106,7 @@ public interface Foo { - implements equals but with 2 args + implements equals but with 2 args, hashCode overloaded as well 0 Date: Sun, 17 Jan 2021 18:32:16 +0100 Subject: [PATCH 95/98] [doc] Update release notes, refs #2959 --- docs/pages/release_notes.md | 7 +++++++ .../src/main/resources/rulesets/releases/6310.xml | 13 +++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 pmd-core/src/main/resources/rulesets/releases/6310.xml diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 9d7eb1bf77..1e338c77d0 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -14,6 +14,12 @@ This is a {{ site.pmd.release_type }} release. ### New and noteworthy +#### New Rules + +* The new Apex rule {% rule "apex/errorprone/OverrideBothEqualsAndHashcode" %} brings the well known Java rule + to Apex. In Apex the same principle applies: `equals` and `hashCode` should always be overridden + together to ensure collection classes such as Maps and Sets work as expected. + ### Fixed Issues * core @@ -28,6 +34,7 @@ This is a {{ site.pmd.release_type }} release. ### External Contributions +* [#2959](https://github.com/pmd/pmd/pull/2959): \[apex] New Rule: override equals and hashcode rule - [recdevs](https://github.com/recdevs) * [#2964](https://github.com/pmd/pmd/pull/2964): \[cs] Update C# grammar for additional C# 7 and C# 8 features - [Maikel Steneker](https://github.com/maikelsteneker) * [#2983](https://github.com/pmd/pmd/pull/2983): \[java] LiteralsFirstInComparisons should consider constant fields - [Ozan Gulle](https://github.com/ozangulle) * [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings - [Vincent Maurin](https://github.com/vmaurin) diff --git a/pmd-core/src/main/resources/rulesets/releases/6310.xml b/pmd-core/src/main/resources/rulesets/releases/6310.xml new file mode 100644 index 0000000000..eaf61d2469 --- /dev/null +++ b/pmd-core/src/main/resources/rulesets/releases/6310.xml @@ -0,0 +1,13 @@ + + + + +This ruleset contains links to rules that are new in PMD v6.31.0 + + + + + From 7e9f17658c8b9fbd564bcb57cf37c27436188657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 18 Jan 2021 14:54:33 +0100 Subject: [PATCH 96/98] Fixups for #2666 Support full swift 5 string grammar - arbitrary balanced delimiters - raw strings ignore escapes - multiline raw strings Also rename lexer rules, 'dashed' is not adapted --- .../sourceforge/pmd/lang/swift/antlr4/Swift.g4 | 18 ++++++++---------- .../pmd/lang/swift/cpd/testdata/Swift5.0.swift | 7 ++++++- .../pmd/lang/swift/cpd/testdata/Swift5.0.txt | 15 ++++++++++----- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 b/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 index c71b5fbda7..2c44bc62f2 100644 --- a/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 +++ b/pmd-swift/src/main/antlr4/net/sourceforge/pmd/lang/swift/antlr4/Swift.g4 @@ -1018,7 +1018,7 @@ ImplicitParameterName : '$' DecimalLiteral ; // TODO: don't allow '_' here // GRAMMAR OF A LITERAL booleanLiteral: BooleanLiteral ; -literal : numericLiteral | MultiStringLiteral | DashedSingleStringLiteral | SingleStringLiteral | BooleanLiteral | NilLiteral ; +literal : numericLiteral | MultiStringLiteral | SingleStringLiteral | BooleanLiteral | NilLiteral | RawMultiStringLiteral | RawSingleStringLiteral ; // GRAMMAR OF AN INTEGER LITERAL @@ -1073,17 +1073,15 @@ TRIPLEDQUOTES : '"""' ; MultiStringLiteral : TRIPLEDQUOTES '\n' .*? '\n' TRIPLEDQUOTES ; fragment MultiQuotedText : MultiQuotedTextItem+ ; -fragment MultiQuotedTextItem : MultiInterpolatedString - | ~[\\\u000A\u000D] - ; +fragment MultiQuotedTextItem : MultiInterpolatedString | ~[\\\u000A\u000D] ; fragment MultiInterpolatedString: '\\(' (MultiQuotedTextItem | SingleStringLiteral)* ')'; -DashedSingleStringLiteral : '#"' DashedMultiQuotedText? '"#' ; -fragment DashedMultiQuotedText : DashedMultiQuotedTextItem+ ; -fragment DashedMultiQuotedTextItem : EscapedCharacter | DashedMultiInterpolatedString - | ~[\\\u000A\u000D] - ; -fragment DashedMultiInterpolatedString: '\\#(' (DashedMultiQuotedTextItem | DashedSingleStringLiteral)* ')'; +// swift 5 extended delimiter, eg ##"abc"## +RawSingleStringLiteral : '#"' RawSingleQuotedTextItem* '"#' | '#' RawSingleStringLiteral '#'; +fragment RawSingleQuotedTextItem : ~[\u000A\u000D] ; + +RawMultiStringLiteral : '#"""' RawMultiQuotedTextItem* '"""#' | '#' RawMultiStringLiteral '#'; +fragment RawMultiQuotedTextItem : . ; // StringLiteral : '"' QuotedText? '"' ; SingleStringLiteral : '"' QuotedText? '"' ; diff --git a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift index cf326c21c7..85bd015309 100644 --- a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift +++ b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.swift @@ -43,6 +43,11 @@ print(x[keyPath: id]) // prints 3 let rawString = #"Press "Continue" to close this dialog."# extension URL { func html(withTitle title: String) -> String { - return #"\#(title)"# + return ##"\#(title)"## } } + +let rawMultiString = ###"a\###"### +let rawMultiString2 = ###"""a\### +""hey"" +"""### diff --git a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt index 0a89406a83..cb66d0c607 100644 --- a/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt +++ b/pmd-swift/src/test/resources/net/sourceforge/pmd/lang/swift/cpd/testdata/Swift5.0.txt @@ -220,14 +220,19 @@ L45 [{] 50 50 L46 [return] 9 14 - [#"\\[ 16 61 + [##" Date: Mon, 18 Jan 2021 15:18:05 +0100 Subject: [PATCH 97/98] Update release notes, refs #2666 --- docs/pages/release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 1e338c77d0..2ab681546c 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -34,6 +34,7 @@ This is a {{ site.pmd.release_type }} release. ### External Contributions +* [#2666](https://github.com/pmd/pmd/pull/2666): \[swift] Manage swift5 string literals - [kenji21](https://github.com/kenji21) * [#2959](https://github.com/pmd/pmd/pull/2959): \[apex] New Rule: override equals and hashcode rule - [recdevs](https://github.com/recdevs) * [#2964](https://github.com/pmd/pmd/pull/2964): \[cs] Update C# grammar for additional C# 7 and C# 8 features - [Maikel Steneker](https://github.com/maikelsteneker) * [#2983](https://github.com/pmd/pmd/pull/2983): \[java] LiteralsFirstInComparisons should consider constant fields - [Ozan Gulle](https://github.com/ozangulle) From 8759ccdce47b9bec3c2761cda3413c3609a75186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 18 Jan 2021 15:32:51 +0100 Subject: [PATCH 98/98] Update release notes, refs #2965 --- docs/pages/release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 2ab681546c..cfa32a64ae 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -37,6 +37,7 @@ This is a {{ site.pmd.release_type }} release. * [#2666](https://github.com/pmd/pmd/pull/2666): \[swift] Manage swift5 string literals - [kenji21](https://github.com/kenji21) * [#2959](https://github.com/pmd/pmd/pull/2959): \[apex] New Rule: override equals and hashcode rule - [recdevs](https://github.com/recdevs) * [#2964](https://github.com/pmd/pmd/pull/2964): \[cs] Update C# grammar for additional C# 7 and C# 8 features - [Maikel Steneker](https://github.com/maikelsteneker) +* [#2965](https://github.com/pmd/pmd/pull/2965): \[cs] Improvements for ignore sequences of literals functionality - [Maikel Steneker](https://github.com/maikelsteneker) * [#2983](https://github.com/pmd/pmd/pull/2983): \[java] LiteralsFirstInComparisons should consider constant fields - [Ozan Gulle](https://github.com/ozangulle) * [#2994](https://github.com/pmd/pmd/pull/2994): \[core] Fix code climate severity strings - [Vincent Maurin](https://github.com/vmaurin)