From ddf55c7f8115ba79a597cabfd1e629ac3822df32 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Thu, 22 Oct 2020 11:30:45 -0700 Subject: [PATCH 001/299] 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 002/299] 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 91936b000b8e94c2a7505a438ffe28212179d9fc Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 26 Oct 2020 19:06:21 +0100 Subject: [PATCH 003/299] [core] Fix XMLRenderer with UTF-16 When using UTF-16 as encoding, the XMLRenderer produced invalid XML: When inserting the linebreak encoded with the given encoding "UTF-16", a BOM was created. This inserted additional characters U+FEFF in the middle of the file, which is not allowed. A partial workaround for this issue would be, to use "UTF-16BE" as encoding instead. This doesn't create a BOM. However, the resulting XML file is then completely without a BOM. --- .../pmd/renderers/XMLRenderer.java | 25 ++++++++++++++++++- .../pmd/renderers/XMLRendererTest.java | 6 +++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java index 840b786ca6..204fc4f31a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java @@ -10,6 +10,8 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.text.SimpleDateFormat; import java.util.Date; @@ -66,7 +68,8 @@ public class XMLRenderer extends AbstractIncrementingRenderer { @Override public void start() throws IOException { String encoding = getProperty(ENCODING); - lineSeparator = PMD.EOL.getBytes(encoding); + String unmarkedEncoding = toUnmarkedEncoding(encoding); + lineSeparator = PMD.EOL.getBytes(unmarkedEncoding); try { xmlWriter.writeStartDocument(encoding, "1.0"); @@ -86,6 +89,26 @@ public class XMLRenderer extends AbstractIncrementingRenderer { } } + /** + * Return a encoding, which doesn't write a BOM (byte order mark). + * Only UTF-16 encoders might write a BOM, see {@link Charset}. + * + *

This is needed, so that we don't accidentally add BOMs whenever + * we insert a newline. + * + * @return + */ + private static String toUnmarkedEncoding(String encoding) { + if (StandardCharsets.UTF_16.name().equalsIgnoreCase(encoding)) { + return StandardCharsets.UTF_16BE.name(); + } + // edge case: UTF-16LE with BOM + if ("UTF-16LE_BOM".equalsIgnoreCase(encoding)) { + return StandardCharsets.UTF_16LE.name(); + } + return encoding; + } + /** * Outputs a platform dependent line separator. * 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 e72e44561f..62e628c106 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 @@ -123,6 +123,12 @@ public class XMLRendererTest extends AbstractRendererTest { verifyXmlEscaping(renderer, "\ud801\udc1c", StandardCharsets.UTF_8); } + @Test + public void testXMLEscapingWithUTF16() throws Exception { + Renderer renderer = getRenderer(); + verifyXmlEscaping(renderer, "𐐜", StandardCharsets.UTF_16); + } + @Test public void testXMLEscapingWithoutUTF8() throws Exception { Renderer renderer = getRenderer(); From 299e2e817d443254b0f7d5fd9eee1efdd385b2df Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 26 Oct 2020 19:32:34 +0100 Subject: [PATCH 004/299] [doc][skip ci] Update release notes --- 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..4e56f1b297 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 + * [#2874](https://github.com/pmd/pmd/pull/2874): \[core] Fix XMLRenderer with UTF-16 + ### API Changes ### External Contributions From f70b2e44fca1b364f14faa52a2eb456f7f2ecc76 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Tue, 27 Oct 2020 16:23:14 -0700 Subject: [PATCH 005/299] 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 006/299] 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 007/299] 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 77273513e6f5102127eef8202793c836793e372a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 25 Sep 2020 16:16:50 +0200 Subject: [PATCH 008/299] Add a fun to check for annotations --- .../java/internal/JavaLanguageHandler.java | 8 +++-- ...ction.java => BaseContextNodeTestFun.java} | 34 ++++++++++++------- .../resources/category/java/codestyle.xml | 4 +-- 3 files changed, 29 insertions(+), 17 deletions(-) rename pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/{TypeIsFunction.java => BaseContextNodeTestFun.java} (53%) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java index ef94005886..03165f7bdf 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java @@ -18,9 +18,9 @@ import net.sourceforge.pmd.lang.java.ast.internal.ReportingStrategy; import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey; import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey; import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleViolationFactory; +import net.sourceforge.pmd.lang.java.rule.xpath.internal.BaseContextNodeTestFun; import net.sourceforge.pmd.lang.java.rule.xpath.internal.GetCommentOnFunction; import net.sourceforge.pmd.lang.java.rule.xpath.internal.MetricFunction; -import net.sourceforge.pmd.lang.java.rule.xpath.internal.TypeIsFunction; import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; import net.sourceforge.pmd.lang.metrics.MetricKey; import net.sourceforge.pmd.lang.metrics.internal.AbstractLanguageMetricsProvider; @@ -32,8 +32,10 @@ public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler { private static final XPathHandler XPATH_HANDLER = XPathHandler.getHandlerForFunctionDefs( - TypeIsFunction.TYPE_IS_EXACTLY, - TypeIsFunction.TYPE_IS, + BaseContextNodeTestFun.TYPE_IS_EXACTLY, + BaseContextNodeTestFun.TYPE_IS, + BaseContextNodeTestFun.HAS_ANNOTATION, + BaseContextNodeTestFun.HAS_MODIFIER, MetricFunction.INSTANCE, GetCommentOnFunction.INSTANCE ); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java similarity index 53% rename from pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunction.java rename to pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java index a339bd4b43..48a239d307 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java @@ -7,6 +7,10 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; import java.util.function.BiPredicate; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.AccessNode; +import net.sourceforge.pmd.lang.java.ast.Annotatable; +import net.sourceforge.pmd.lang.java.ast.JModifier; +import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode; @@ -18,7 +22,6 @@ import net.sf.saxon.trans.XPathException; import net.sf.saxon.value.BooleanValue; import net.sf.saxon.value.SequenceType; - /** * XPath function {@code pmd-java:typeIs(typeName as xs:string) as xs:boolean} * and {@code typeIsExactly}. @@ -27,30 +30,33 @@ import net.sf.saxon.value.SequenceType; * *

Returns true if the type of the node matches, false otherwise. */ -public final class TypeIsFunction extends BaseJavaXPathFunction { +public class BaseContextNodeTestFun extends BaseJavaXPathFunction { - public static final TypeIsFunction TYPE_IS_EXACTLY = new TypeIsFunction("typeIsExactly", TypeTestUtil::isExactlyA); - public static final TypeIsFunction TYPE_IS = new TypeIsFunction("typeIs", TypeTestUtil::isA); + private static final SequenceType[] ARGTYPES = {SequenceType.SINGLE_STRING}; + private final Class klass; + private final BiPredicate checker; - private final BiPredicate checker; + public static final BaseJavaXPathFunction TYPE_IS_EXACTLY = new BaseContextNodeTestFun<>(TypeNode.class, "typeIsExactly", TypeTestUtil::isExactlyA); + public static final BaseJavaXPathFunction TYPE_IS = new BaseContextNodeTestFun<>(TypeNode.class, "typeIs", TypeTestUtil::isA); + public static final BaseJavaXPathFunction HAS_ANNOTATION = new BaseContextNodeTestFun<>(Annotatable.class, "hasAnnotation", (name, node) -> node.isAnnotationPresent(name)); + public static final BaseJavaXPathFunction HAS_MODIFIER = new BaseContextNodeTestFun<>(AccessNode.class, "hasModifier", (name, node) -> node.hasModifiers(JModifier.fromToken(name))); - private TypeIsFunction(String localName, BiPredicate checker) { + protected BaseContextNodeTestFun(Class klass, String localName, BiPredicate checker) { super(localName); + this.klass = klass; this.checker = checker; } @Override public SequenceType[] getArgumentTypes() { - return new SequenceType[] {SequenceType.SINGLE_STRING}; + return ARGTYPES; } - @Override public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { return SequenceType.SINGLE_BOOLEAN; } - @Override public boolean dependsOnFocus() { return true; @@ -64,10 +70,14 @@ public final class TypeIsFunction extends BaseJavaXPathFunction { Node contextNode = ((AstElementNode) context.getContextItem()).getUnderlyingNode(); String fullTypeName = arguments[0].head().getStringValue(); - if (contextNode instanceof TypeNode) { - return BooleanValue.get(checker.test(fullTypeName, (TypeNode) contextNode)); + + if (klass.isInstance(contextNode)) { + return BooleanValue.get(checker.test(fullTypeName, (T) contextNode)); } else { - throw new IllegalArgumentException("typeIs function may only be called on a TypeNode."); + throw new IllegalArgumentException( + getFunctionQName().getLocalPart() + + " function may only be called on an instance of " + + klass.getSimpleName()); } } }; diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index fc8bcbb593..e8a688dba3 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -173,8 +173,8 @@ prefix for these methods. [starts-with(@Name, 'get')] [@Arity = 0 or $checkParameterizedMethods = true()] [ - ResultType/Type/PrimitiveType[@Image = 'boolean'] - and not(../Annotation//Name[@Image = 'Override']) + PrimitiveType[@Kind = 'boolean'] + and not(ModifierList/Annotation//Name[@Image = 'Override']) ] ]]> From 39e84b7f2fb44daa92e57f70d3a338e855ee0a86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 25 Sep 2020 17:05:55 +0200 Subject: [PATCH 009/299] Port some codestyle XPath rules --- .../resources/category/java/codestyle.xml | 40 +++++++++---------- .../codestyle/BooleanGetMethodNameTest.java | 1 - .../codestyle/CallSuperInConstructorTest.java | 1 - .../codestyle/ControlStatementBracesTest.java | 1 - .../codestyle/xml/ControlStatementBraces.xml | 2 +- 5 files changed, 19 insertions(+), 26 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index e8a688dba3..630e775baa 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -172,10 +172,7 @@ prefix for these methods. //MethodDeclaration [starts-with(@Name, 'get')] [@Arity = 0 or $checkParameterizedMethods = true()] - [ - PrimitiveType[@Kind = 'boolean'] - and not(ModifierList/Annotation//Name[@Image = 'Override']) - ] + [ PrimitiveType[@Kind = 'boolean'] and not(pmd-java:hasAnnotation('java.lang.Override')) ] ]]> @@ -206,9 +203,8 @@ another constructor (such as an overloaded constructor) is called, this rule wil @@ -355,27 +351,27 @@ boolean bar(int x, int y) { 1 and not(self::Block or self::IfStatement)] + [ $checkSingleIfStmt + (: Inside this (...) is the definition of a "single if statement" :) + or not(parent::*/@Else = false() (: No else stmt :) + (: Not the last branch of an 'if ... else if' chain :) + and not(parent::IfStatement[parent::IfStatement]))] + | (: Reports case labels if one of their subordinate statements is not braced :) - //SwitchLabel[$checkCaseStmt] - [count(following-sibling::BlockStatement except following-sibling::SwitchLabel[1]/following-sibling::BlockStatement) > 1 - or (some $stmt (: in only the block statements until the next label :) - in following-sibling::BlockStatement except following-sibling::SwitchLabel[1]/following-sibling::BlockStatement - satisfies not($stmt/Statement/Block))] + //SwitchFallthroughBranch[$checkCaseStmt] + [count(*) > 1 and (count(*) > 2 or not(child::*[2]/self::Block))] ]]> diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/BooleanGetMethodNameTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/BooleanGetMethodNameTest.java index 2b5d55e37c..c6c6734dc8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/BooleanGetMethodNameTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/BooleanGetMethodNameTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class BooleanGetMethodNameTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/CallSuperInConstructorTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/CallSuperInConstructorTest.java index 38a6eb2be2..05b5cf0c8c 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/CallSuperInConstructorTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/CallSuperInConstructorTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class CallSuperInConstructorTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ControlStatementBracesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ControlStatementBracesTest.java index cfaee67e57..c7ff86946e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ControlStatementBracesTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ControlStatementBracesTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ControlStatementBracesTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/ControlStatementBraces.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/ControlStatementBraces.xml index ba3d513697..6a950e25b4 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/ControlStatementBraces.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/ControlStatementBraces.xml @@ -468,7 +468,7 @@ public class Foo { - Case, dangling unbraced statement + Case, dangling unbraced statement 2 true 1 6 From 248a3127b8546f78275371622cbb30391ffa08b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 25 Sep 2020 17:07:59 +0200 Subject: [PATCH 010/299] Update saxon version Fixes bug https://saxonica.plan.io/issues/4610\?pn\=1\#change-16066 which occurs because of property inlining --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 603bdbaea7..21b0ffdaf0 100644 --- a/pom.xml +++ b/pom.xml @@ -683,7 +683,7 @@ net.sf.saxon Saxon-HE - 10.1 + 10.2 org.mozilla From fa8cc3ff990cc9c2acb32e3ca9cedf59ce969280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 25 Sep 2020 17:16:10 +0200 Subject: [PATCH 011/299] Update more rules DefaultPackage --- .../resources/category/java/codestyle.xml | 28 +++++++++---------- .../rule/codestyle/DefaultPackageTest.java | 1 - .../rule/codestyle/xml/DefaultPackage.xml | 1 + 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 630e775baa..c8b3629230 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -404,21 +404,19 @@ The rule allows methods and fields annotated with Guava's @VisibleForTesting and diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DefaultPackageTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DefaultPackageTest.java index 03e4db86ca..19c6c714fd 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DefaultPackageTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/DefaultPackageTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DefaultPackageTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/DefaultPackage.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/DefaultPackage.xml index 7a9b4a83f9..443844b561 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/DefaultPackage.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/DefaultPackage.xml @@ -59,6 +59,7 @@ public class Foo { #1410 DefaultPackage triggers on field annotated with @VisibleForTesting 0 Date: Fri, 25 Sep 2020 17:36:34 +0200 Subject: [PATCH 012/299] Update DontImportJavaLang Convert to xpath rule --- .../codestyle/DontImportJavaLangRule.java | 32 ------------------- .../resources/category/java/codestyle.xml | 13 +++++++- .../codestyle/DontImportJavaLangTest.java | 1 - 3 files changed, 12 insertions(+), 34 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/DontImportJavaLangRule.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/DontImportJavaLangRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/DontImportJavaLangRule.java deleted file mode 100644 index 9afbe90da8..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/DontImportJavaLangRule.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.codestyle; - -import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -public class DontImportJavaLangRule extends AbstractJavaRule { - private static final String IMPORT_JAVA_LANG = "java.lang"; - - @Override - public Object visit(ASTImportDeclaration node, Object data) { - - if (node.isStatic()) { - return data; - } - - String img = node.getChild(0).getImage(); - if (img.startsWith(IMPORT_JAVA_LANG)) { - if (!IMPORT_JAVA_LANG.equals(img)) { - if (img.indexOf('.', IMPORT_JAVA_LANG.length() + 1) != -1 || node.isImportOnDemand()) { - // Importing from a subpackage / inner class - return data; - } - } - addViolation(data, node); - } - return data; - } -} diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index c8b3629230..05db9c8fe0 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -428,12 +428,23 @@ The rule allows methods and fields annotated with Guava's @VisibleForTesting and language="java" since="0.5" message="Avoid importing anything from the package 'java.lang'" - class="net.sourceforge.pmd.lang.java.rule.codestyle.DontImportJavaLangRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_codestyle.html#dontimportjavalang"> Avoid importing anything from the package 'java.lang'. These classes are automatically imported (JLS 7.5.3). 4 + + + + + + Date: Sun, 27 Sep 2020 16:19:36 +0200 Subject: [PATCH 013/299] Use getModifiers funs instead --- .../java/internal/JavaLanguageHandler.java | 4 +- .../internal/BaseContextNodeTestFun.java | 3 - .../rule/xpath/internal/GetModifiersFun.java | 79 +++++++++++++++++++ 3 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFun.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java index 03165f7bdf..b4f36fb736 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java @@ -20,6 +20,7 @@ import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey; import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleViolationFactory; import net.sourceforge.pmd.lang.java.rule.xpath.internal.BaseContextNodeTestFun; import net.sourceforge.pmd.lang.java.rule.xpath.internal.GetCommentOnFunction; +import net.sourceforge.pmd.lang.java.rule.xpath.internal.GetModifiersFun; import net.sourceforge.pmd.lang.java.rule.xpath.internal.MetricFunction; import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; import net.sourceforge.pmd.lang.metrics.MetricKey; @@ -35,7 +36,8 @@ public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler { BaseContextNodeTestFun.TYPE_IS_EXACTLY, BaseContextNodeTestFun.TYPE_IS, BaseContextNodeTestFun.HAS_ANNOTATION, - BaseContextNodeTestFun.HAS_MODIFIER, + GetModifiersFun.GET_EFFECTIVE, + GetModifiersFun.GET_EXPLICIT, MetricFunction.INSTANCE, GetCommentOnFunction.INSTANCE ); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java index 48a239d307..7ccdd96ab2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java @@ -7,9 +7,7 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; import java.util.function.BiPredicate; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.ast.Annotatable; -import net.sourceforge.pmd.lang.java.ast.JModifier; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; @@ -39,7 +37,6 @@ public class BaseContextNodeTestFun extends BaseJavaXPathFun public static final BaseJavaXPathFunction TYPE_IS_EXACTLY = new BaseContextNodeTestFun<>(TypeNode.class, "typeIsExactly", TypeTestUtil::isExactlyA); public static final BaseJavaXPathFunction TYPE_IS = new BaseContextNodeTestFun<>(TypeNode.class, "typeIs", TypeTestUtil::isA); public static final BaseJavaXPathFunction HAS_ANNOTATION = new BaseContextNodeTestFun<>(Annotatable.class, "hasAnnotation", (name, node) -> node.isAnnotationPresent(name)); - public static final BaseJavaXPathFunction HAS_MODIFIER = new BaseContextNodeTestFun<>(AccessNode.class, "hasModifier", (name, node) -> node.hasModifiers(JModifier.fromToken(name))); protected BaseContextNodeTestFun(Class klass, String localName, BiPredicate checker) { super(localName); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFun.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFun.java new file mode 100644 index 0000000000..961e75c96f --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFun.java @@ -0,0 +1,79 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.xpath.internal; + +import java.util.Set; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTModifierList; +import net.sourceforge.pmd.lang.java.ast.AccessNode; +import net.sourceforge.pmd.lang.java.ast.JModifier; +import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode; +import net.sourceforge.pmd.util.CollectionUtil; + +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.lib.ExtensionFunctionCall; +import net.sf.saxon.om.Sequence; +import net.sf.saxon.value.EmptySequence; +import net.sf.saxon.value.SequenceExtent; +import net.sf.saxon.value.SequenceType; +import net.sf.saxon.value.StringValue; + +/** + * XPath function {@code pmd-java:typeIs(typeName as xs:string) as xs:boolean} + * and {@code typeIsExactly}. + * + *

Example XPath 2.0: {@code //ClassOrInterfaceType[pmd-java:typeIs('java.lang.String')]} + * + *

Returns true if the type of the node matches, false otherwise. + */ +public class GetModifiersFun extends BaseJavaXPathFunction { + + private static final SequenceType[] ARGTYPES = {}; + private final boolean explicit; + + public static final GetModifiersFun GET_EFFECTIVE = new GetModifiersFun("modifiers", false); + public static final GetModifiersFun GET_EXPLICIT = new GetModifiersFun("explicitModifiers", true); + + private GetModifiersFun(String localName, boolean explicit) { + super(localName); + this.explicit = explicit; + } + + @Override + public SequenceType[] getArgumentTypes() { + return ARGTYPES; + } + + @Override + public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { + return SequenceType.STRING_SEQUENCE; + } + + @Override + public boolean dependsOnFocus() { + return true; + } + + @Override + public ExtensionFunctionCall makeCallExpression() { + return new ExtensionFunctionCall() { + @Override + public Sequence call(XPathContext context, Sequence[] arguments) { + Node contextNode = ((AstElementNode) context.getContextItem()).getUnderlyingNode(); + + + if (contextNode instanceof AccessNode) { + ASTModifierList modList = ((AccessNode) contextNode).getModifiers(); + Set mods = explicit ? modList.getExplicitModifiers() + : modList.getEffectiveModifiers(); + return new SequenceExtent(CollectionUtil.map(mods, mod -> new StringValue(mod.getToken()))); + } else { + return EmptySequence.getInstance(); + } + } + }; + } +} From f750dd0c355de1f88e8c2483293ee0668d263d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 16:34:44 +0200 Subject: [PATCH 014/299] Update EmptyMethodInAbstractClassShouldBeAbstract --- .../resources/category/java/codestyle.xml | 19 ++++++------------- ...odInAbstractClassShouldBeAbstractTest.java | 1 - ...yMethodInAbstractClassShouldBeAbstract.xml | 11 +++++++++++ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 05db9c8fe0..3c6775ef27 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -494,20 +494,13 @@ usage by developers who should be implementing their own versions in the concret diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/EmptyMethodInAbstractClassShouldBeAbstractTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/EmptyMethodInAbstractClassShouldBeAbstractTest.java index a9fcbdc7dd..8d2ca65fdc 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/EmptyMethodInAbstractClassShouldBeAbstractTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/EmptyMethodInAbstractClassShouldBeAbstractTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyMethodInAbstractClassShouldBeAbstractTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/EmptyMethodInAbstractClassShouldBeAbstract.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/EmptyMethodInAbstractClassShouldBeAbstract.xml index d21dc2a7d2..aec3caa4d8 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/EmptyMethodInAbstractClassShouldBeAbstract.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/EmptyMethodInAbstractClassShouldBeAbstract.xml @@ -202,6 +202,17 @@ public abstract class Foo { public void isVisible() { ; } +} + ]]> + + + Dont flag default methods + 0 + From fbafb8d6e118c36b7453826bbee0d194f12f7129 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 16:35:52 +0200 Subject: [PATCH 015/299] Update ExtendsObject --- pmd-java/src/main/resources/category/java/codestyle.xml | 2 +- .../pmd/lang/java/rule/codestyle/ExtendsObjectTest.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 3c6775ef27..7914029df8 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -533,7 +533,7 @@ public abstract class ShouldBeAbstract { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ExtendsObjectTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ExtendsObjectTest.java index 9dadb1123c..d961107c92 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ExtendsObjectTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ExtendsObjectTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ExtendsObjectTest extends PmdRuleTst { // no additional unit tests } From 7c37fe67fe877ea64acf3805a84392a97b0e690e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 16:37:58 +0200 Subject: [PATCH 016/299] Update ForLoopShouldBeWhileLoop --- pmd-java/src/main/resources/category/java/codestyle.xml | 6 +----- .../java/rule/codestyle/ForLoopShouldBeWhileLoopTest.java | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 7914029df8..9eef4ff4a3 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -626,11 +626,7 @@ Some for loops can be simplified to while loops, this makes them more concise. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ForLoopShouldBeWhileLoopTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ForLoopShouldBeWhileLoopTest.java index 3bc2994c77..646e03a853 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ForLoopShouldBeWhileLoopTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ForLoopShouldBeWhileLoopTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ForLoopShouldBeWhileLoopTest extends PmdRuleTst { // no additional unit tests } From 8db49519ac31e41961bf7b404b798617e531573b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 16:39:42 +0200 Subject: [PATCH 017/299] Update GenericsNaming --- pmd-java/src/main/resources/category/java/codestyle.xml | 6 +++--- .../pmd/lang/java/rule/codestyle/GenericsNamingTest.java | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 9eef4ff4a3..07bd2a1143 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -696,10 +696,10 @@ Names for references to generic values should be limited to a single uppercase l 1 +//TypeParameter[ + string-length(@Name) > 1 or - upper-case(@Image) != @Image + upper-case(@Name) != @Name ] ]]> diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/GenericsNamingTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/GenericsNamingTest.java index 8b9f5e59c8..f7357c2542 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/GenericsNamingTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/GenericsNamingTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class GenericsNamingTest extends PmdRuleTst { // no additional unit tests } From 233e9299a6194e0d3ef6d5fac004fa7823b6fc53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 16:45:22 +0200 Subject: [PATCH 018/299] Update EJB naming conventions Also allow TypeTestUtil to not fail if classpath is incomplete --- .../lang/java/internal/JavaAstProcessor.java | 4 ++ .../internal/UnresolvedClassStore.java | 4 ++ .../pmd/lang/java/types/TypeOps.java | 2 +- .../pmd/lang/java/types/TypeTestUtil.java | 5 +- .../lang/java/types/TypesFromReflection.java | 52 +++++++++---------- .../resources/category/java/codestyle.xml | 35 +++---------- .../LocalHomeNamingConventionTest.java | 1 - ...lInterfaceSessionNamingConventionTest.java | 1 - ...MDBAndSessionBeanNamingConventionTest.java | 1 - .../xml/MDBAndSessionBeanNamingConvention.xml | 4 ++ 10 files changed, 51 insertions(+), 58 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaAstProcessor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaAstProcessor.java index 7598a3ccbf..ec1cdc1c30 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaAstProcessor.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaAstProcessor.java @@ -84,6 +84,10 @@ public final class JavaAstProcessor { unresolvedTypes = new UnresolvedClassStore(typeSystem); } + public UnresolvedClassStore getUnresolvedStore() { + return unresolvedTypes; + } + static TypeInferenceLogger defaultTypeInfLogger() { if (INFERENCE_LOG_LEVEL == Level.FINEST) { return new VerboseLogger(System.err); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/UnresolvedClassStore.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/UnresolvedClassStore.java index 97796c5b73..3f3192e75b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/UnresolvedClassStore.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/UnresolvedClassStore.java @@ -69,4 +69,8 @@ public final class UnresolvedClassStore { return makeUnresolvedReference(qualifier.getCanonicalName() + '.' + simpleName, typeArity); } + + public static JClassSymbol untrackedUnresolvedReference(TypeSystem ts, String canonicalName) { + return new FlexibleUnresolvedClassImpl(ts, null, canonicalName); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java index 582e8ca601..9a9b709e89 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java @@ -1606,7 +1606,7 @@ public final class TypeOps { return res; } else { // then look in interfaces if possible - if (target.isInterface()) { + if (target.isInterface() || target.isUnresolved()) { return firstResult(target, t.getSuperInterfaces()); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index cc736366ea..c538b66d66 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -11,10 +11,12 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.internal.util.AssertionUtil; +import net.sourceforge.pmd.lang.java.ast.InternalApiBridge; import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; +import net.sourceforge.pmd.lang.java.symbols.internal.UnresolvedClassStore; /** * Public utilities to test the type of nodes. @@ -121,7 +123,8 @@ public final class TypeTestUtil { } TypeSystem ts = thisType.getTypeSystem(); - @Nullable JTypeMirror otherType = TypesFromReflection.loadType(ts, canonicalName); + UnresolvedClassStore unresolvedStore = InternalApiBridge.getProcessor(node).getUnresolvedStore(); + @Nullable JTypeMirror otherType = TypesFromReflection.loadType(ts, canonicalName, unresolvedStore); if (otherType == null) { return false; // we know isExactlyA(canonicalName, node); returned false } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypesFromReflection.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypesFromReflection.java index 4ca6c786cc..f2f89e319f 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypesFromReflection.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypesFromReflection.java @@ -11,9 +11,7 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.lang.reflect.WildcardType; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import org.apache.commons.lang3.StringUtils; @@ -24,6 +22,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.internal.UnresolvedClassStore; import net.sourceforge.pmd.util.CollectionUtil; /** @@ -186,42 +185,35 @@ public final class TypesFromReflection { return ts.wildcard(isUpper, ts.glb(boundsMapped)); } - - - /** Maps names of primitives to their corresponding primitive {@code Class}es. */ - private static final Map> PRIMITIVES_BY_NAME = new HashMap<>(); - - - static { - PRIMITIVES_BY_NAME.put("boolean", Boolean.TYPE); - PRIMITIVES_BY_NAME.put("byte", Byte.TYPE); - PRIMITIVES_BY_NAME.put("char", Character.TYPE); - PRIMITIVES_BY_NAME.put("short", Short.TYPE); - PRIMITIVES_BY_NAME.put("int", Integer.TYPE); - PRIMITIVES_BY_NAME.put("long", Long.TYPE); - PRIMITIVES_BY_NAME.put("double", Double.TYPE); - PRIMITIVES_BY_NAME.put("float", Float.TYPE); - PRIMITIVES_BY_NAME.put("void", Void.TYPE); - } - - /** * Load a class. Supports loading array types like 'java.lang.String[]' and * converting a canonical name to a binary name (eg 'java.util.Map.Entry' -> * 'java.util.Map$Entry'). */ public static @Nullable JTypeMirror loadType(TypeSystem ctr, String className) { - return loadClassMaybeArray(ctr, StringUtils.deleteWhitespace(className)); + return loadType(ctr, className, null); + } + + /** + * Load a class. Supports loading array types like 'java.lang.String[]' and + * converting a canonical name to a binary name (eg 'java.util.Map.Entry' -> + * 'java.util.Map$Entry'). Types that are not on the classpath may + * be replaced by placeholder types if the {@link UnresolvedClassStore} + * parameter is non-null. + */ + public static @Nullable JTypeMirror loadType(TypeSystem ctr, String className, UnresolvedClassStore unresolvedStore) { + return loadClassMaybeArray(ctr, StringUtils.deleteWhitespace(className), unresolvedStore); } public static @Nullable JClassSymbol loadSymbol(TypeSystem ctr, String className) { - JTypeMirror type = loadClassMaybeArray(ctr, StringUtils.deleteWhitespace(className)); + JTypeMirror type = loadType(ctr, className); return type == null ? null : (JClassSymbol) type.getSymbol(); } private static @Nullable JTypeMirror loadClassMaybeArray(TypeSystem ts, - String className) { + String className, + @Nullable UnresolvedClassStore unresolvedClassStore) { Validate.notNull(className, "className must not be null."); if (className.endsWith("[]")) { int dimension = 0; @@ -234,7 +226,7 @@ public final class TypesFromReflection { checkJavaIdent(className, i); String elementName = className.substring(0, i); - @Nullable JClassSymbol elementType = ts.getClassSymbolFromCanonicalName(elementName); + JClassSymbol elementType = getClassOrDefault(ts, unresolvedClassStore, elementName); if (elementType == null) { return null; } @@ -242,10 +234,18 @@ public final class TypesFromReflection { return ts.arrayType(ts.rawType(elementType), dimension); } else { checkJavaIdent(className, className.length()); - return ts.rawType(ts.getClassSymbolFromCanonicalName(className)); + return ts.rawType(getClassOrDefault(ts, unresolvedClassStore, className)); } } + private static JClassSymbol getClassOrDefault(TypeSystem ts, @Nullable UnresolvedClassStore unresolvedClassStore, String canonicalName) { + JClassSymbol loaded = ts.getClassSymbolFromCanonicalName(canonicalName); + if (loaded == null && unresolvedClassStore != null) { + loaded = unresolvedClassStore.makeUnresolvedReference(canonicalName, 0); + } + return loaded; + } + private static IllegalArgumentException invalidClassName(String className) { return new IllegalArgumentException("Not a valid class name \"" + className + "\""); } diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 07bd2a1143..983725f3bc 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -838,14 +838,8 @@ The Local Home interface of a Session EJB should be suffixed by 'LocalHome'. @@ -876,14 +870,8 @@ The Local Interface of a Session EJB should be suffixed by 'Local'. @@ -1005,18 +993,11 @@ The EJB Specification states that any MessageDrivenBean or SessionBean should be diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalHomeNamingConventionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalHomeNamingConventionTest.java index 2518a739b9..11d40a1788 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalHomeNamingConventionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalHomeNamingConventionTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LocalHomeNamingConventionTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalInterfaceSessionNamingConventionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalInterfaceSessionNamingConventionTest.java index 5ed4ddb40a..be1832f299 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalInterfaceSessionNamingConventionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalInterfaceSessionNamingConventionTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LocalInterfaceSessionNamingConventionTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MDBAndSessionBeanNamingConventionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MDBAndSessionBeanNamingConventionTest.java index d9c5373a22..e12899a61d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MDBAndSessionBeanNamingConventionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MDBAndSessionBeanNamingConventionTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class MDBAndSessionBeanNamingConventionTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MDBAndSessionBeanNamingConvention.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MDBAndSessionBeanNamingConvention.xml index 635299aad2..de4750a949 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MDBAndSessionBeanNamingConvention.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MDBAndSessionBeanNamingConvention.xml @@ -8,6 +8,7 @@ Bad SessionBean name 1 @@ -16,6 +17,7 @@ public class SomeClass implements SessionBean {} Bad MessageDrivenBean name 1 @@ -24,6 +26,7 @@ public class SomeClass implements MessageDrivenBean {} Good SessionBean name 0 @@ -32,6 +35,7 @@ public class SomeBean implements SessionBean {} Good MessageDrivenBean name 0 From d1b90778d771426d0de6e4168cf59630873bd4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 17:12:09 +0200 Subject: [PATCH 019/299] Update LongVariable --- .../pmd/lang/java/rule/codestyle/LongVariableTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LongVariableTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LongVariableTest.java index cf115f9d72..793af6fa63 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LongVariableTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LongVariableTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LongVariableTest extends PmdRuleTst { // no additional unit tests } From 9768c3928baae7911d9c5592eefed187cc76df5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 17:17:29 +0200 Subject: [PATCH 020/299] Update UseUnderscoresInNumericLiterals --- pmd-java/src/main/resources/category/java/codestyle.xml | 9 ++------- .../codestyle/UseUnderscoresInNumericLiteralsTest.java | 1 - 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 983725f3bc..a70f8800e0 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -1106,14 +1106,9 @@ public class ClassInDefaultPackage { Date: Sun, 27 Sep 2020 17:19:50 +0200 Subject: [PATCH 021/299] Update PackageCase --- pmd-java/src/main/resources/category/java/codestyle.xml | 2 +- .../pmd/lang/java/rule/codestyle/PackageCaseTest.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index a70f8800e0..9f8add948e 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -1172,7 +1172,7 @@ Detects when a package definition contains uppercase characters. 3 - //PackageDeclaration/Name[lower-case(@Image)!=@Image] + //PackageDeclaration[lower-case(@Name) != @Name] diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/PackageCaseTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/PackageCaseTest.java index 989872db79..0b59065adb 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/PackageCaseTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/PackageCaseTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class PackageCaseTest extends PmdRuleTst { // no additional unit tests } From 1c4f2153ae9ac718dd305a92ca4990f7ee247b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 17:24:27 +0200 Subject: [PATCH 022/299] Update NoPackage, add nodeIs function --- .../pmd/lang/java/internal/JavaLanguageHandler.java | 1 + .../rule/xpath/internal/BaseContextNodeTestFun.java | 10 ++++++++++ .../src/main/resources/category/java/codestyle.xml | 2 +- .../pmd/lang/java/rule/codestyle/NoPackageTest.java | 1 - 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java index b4f36fb736..14567972b2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java @@ -36,6 +36,7 @@ public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler { BaseContextNodeTestFun.TYPE_IS_EXACTLY, BaseContextNodeTestFun.TYPE_IS, BaseContextNodeTestFun.HAS_ANNOTATION, + BaseContextNodeTestFun.NODE_IS, GetModifiersFun.GET_EFFECTIVE, GetModifiersFun.GET_EXPLICIT, MetricFunction.INSTANCE, diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java index 7ccdd96ab2..c41bfa422e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java @@ -37,6 +37,16 @@ public class BaseContextNodeTestFun extends BaseJavaXPathFun public static final BaseJavaXPathFunction TYPE_IS_EXACTLY = new BaseContextNodeTestFun<>(TypeNode.class, "typeIsExactly", TypeTestUtil::isExactlyA); public static final BaseJavaXPathFunction TYPE_IS = new BaseContextNodeTestFun<>(TypeNode.class, "typeIs", TypeTestUtil::isA); public static final BaseJavaXPathFunction HAS_ANNOTATION = new BaseContextNodeTestFun<>(Annotatable.class, "hasAnnotation", (name, node) -> node.isAnnotationPresent(name)); + public static final BaseJavaXPathFunction NODE_IS = + new BaseContextNodeTestFun<>(JavaNode.class, "nodeIs", (name, node) -> { + Class klass; + try { + klass = Class.forName("net.sourceforge.pmd.lang.java.ast.AST" + name); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("No class AST" + name); + } + return klass.isInstance(node); + }); protected BaseContextNodeTestFun(Class klass, String localName, BiPredicate checker) { super(localName); diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 9f8add948e..5813b8ff53 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -1072,7 +1072,7 @@ Detects when a class, interface, enum or annotation does not have a package defi 3 - /CompilationUnit[not(./PackageDeclaration)]/TypeDeclaration[1] + /CompilationUnit[not(PackageDeclaration)]/*[pmd-java:nodeIs("AnyTypeDeclaration")][1] diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/NoPackageTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/NoPackageTest.java index cd513a7cf9..54b1246e32 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/NoPackageTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/NoPackageTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class NoPackageTest extends PmdRuleTst { // no additional unit tests } From 2802ae4eec4493db0e2afa2e7c44d47c203a9f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 17:30:08 +0200 Subject: [PATCH 023/299] Update xpath fun doc --- docs/_data/xpath_funs.yml | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/docs/_data/xpath_funs.yml b/docs/_data/xpath_funs.yml index 222f3b70f2..80c012fe51 100644 --- a/docs/_data/xpath_funs.yml +++ b/docs/_data/xpath_funs.yml @@ -13,12 +13,31 @@ langs: - name: "Java" ns: "pmd-java" funs: + - name: nodeIs + returnType: "xs:boolean" + shortDescription: "Tests the runtime type of the node instance" + description: "Returns true if the runtime type of the AST node is a subtype of the given class. + Contrary to typeIs, this tests the type of the AST node. + For example, the AST node for a literal (e.g. `5d`) has type ASTNumericLiteral, + and this function will ignore the static type of the expression (double)" + parameters: + - name: nodeClassName + type: "xs:string" + description: "Simple name of a class or interface in package {% jdoc_package :jast %}, without the 'AST' prefix" + examples: + - code: '//*[pmd-java:nodeIs("Expression")]' + outcome: "Matches all nodes that implement ASTExpression" + - code: '//*[pmd-java:nodeIs("AnyTypeDeclaration")]' + outcome: "Matches all nodes that implement ASTAnyTypeDeclaration" + - code: '//*[pmd-java:nodeIs("Foo")]' + outcome: "Runtime error, there's no class ASTFoo in the package" + - name: typeIs returnType: "xs:boolean" shortDescription: "Tests a node's static type" description: "Returns true if the context node's static Java type is a subtype of the given type. This tests for the resolved type of the Java construct, not the type of the AST node. - For example, the AST node for a literal (e.g. `5d`) has type ASTLiteral, however this + For example, the AST node for a literal (e.g. `5d`) has type ASTNumericLiteral, however this function will compare the type of the literal (eg here, `double`) against the argument." notes: *needs_typenode parameters: @@ -29,7 +48,6 @@ langs: - code: '//VariableDeclaratorId[pmd-java:typeIs("java.lang.List")]' outcome: "Matches variable declarators of type `List` or any of its subtypes (including e.g. `ArrayList`)" - - name: typeIsExactly returnType: "xs:boolean" shortDescription: "Tests a node's static type, ignoring subtypes" @@ -53,3 +71,16 @@ langs: - name: "metricKey" type: "xs:string" description: "The name of an enum constant in {% jdoc jmx::api.JavaOperationMetricKey %} or {% jdoc jmx::api.JavaClassMetricKey %}" + + - name: hasAnnotation + returnType: "xs:boolean" + shortDescription: "Tests whether an annotation is present on the node" + description: "Returns true if the node has an annotation with the given qualified name" + notes: "The context node must be an {% jdoc jast::Annotatable %}" + parameters: + - name: annotationClassName + type: "xs:string" + description: "Canonical name of an annotation type" + examples: + - code: '//MethodDeclaration[pmd-java:hasAnnotation("java.lang.Override")]' + outcome: "Matches all method declarations that are annotated with @Override" From ea3f201ec92310a54bf078b3a0c2346aa00c51ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 17:38:52 +0200 Subject: [PATCH 024/299] Update Short/Long vars --- pmd-java/src/main/resources/category/java/codestyle.xml | 8 ++++---- .../pmd/lang/java/rule/codestyle/ShortClassNameTest.java | 1 - .../pmd/lang/java/rule/codestyle/ShortMethodNameTest.java | 1 - .../pmd/lang/java/rule/codestyle/ShortVariableTest.java | 1 - 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 5813b8ff53..5e86a55a6b 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -1372,13 +1372,13 @@ Fields, local variables, or parameter names that are very short are not helpful diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortClassNameTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortClassNameTest.java index e8af416afb..1c4bdc49c6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortClassNameTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortClassNameTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ShortClassNameTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortMethodNameTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortMethodNameTest.java index 94b3735ddf..7133947372 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortMethodNameTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortMethodNameTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ShortMethodNameTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortVariableTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortVariableTest.java index 333dc77c50..2d29ac3442 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortVariableTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ShortVariableTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ShortVariableTest extends PmdRuleTst { // no additional unit tests } From 0aa02f78a83ce1dbe28605bad9ca801cd3a76f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 17:41:34 +0200 Subject: [PATCH 025/299] Update TooManyStaticImports --- .../java/symbols/table/internal/SymTableFactory.java | 3 +++ .../java/rule/codestyle/TooManyStaticImportsTest.java | 1 - .../java/rule/codestyle/xml/TooManyStaticImports.xml | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymTableFactory.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymTableFactory.java index a40a847b76..d0fdf0fd24 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymTableFactory.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymTableFactory.java @@ -256,6 +256,9 @@ final class SymTableFactory { // types, fields or methods having the same name int idx = name.lastIndexOf('.'); + if (idx < 0) { + continue; // invalid syntax + } String className = name.substring(0, idx); JClassSymbol containerClass = loadClassReportFailure(anImport, className); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/TooManyStaticImportsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/TooManyStaticImportsTest.java index 4794450818..43af1eac98 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/TooManyStaticImportsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/TooManyStaticImportsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class TooManyStaticImportsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/TooManyStaticImports.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/TooManyStaticImports.xml index 5ba4e8b6a1..c9fa856aaf 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/TooManyStaticImports.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/TooManyStaticImports.xml @@ -8,11 +8,11 @@ simple violation 1 From 6fff03fdb45fb3bb8d5f6a5d622e47af8003c025 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 17:47:25 +0200 Subject: [PATCH 026/299] Update UselessQualifiedThis --- pmd-java/src/main/resources/category/java/codestyle.xml | 8 ++------ .../java/rule/codestyle/UselessQualifiedThisTest.java | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 5e86a55a6b..9fc7854bef 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -1781,12 +1781,8 @@ public class Foo { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UselessQualifiedThisTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UselessQualifiedThisTest.java index 9425ca6585..19f7b4bf6e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UselessQualifiedThisTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UselessQualifiedThisTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UselessQualifiedThisTest extends PmdRuleTst { // no additional unit tests } From e963576a890bfde56c8a96ab3626c6ef6cb45bbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 17:48:59 +0200 Subject: [PATCH 027/299] Update UseShortArrayInitializer --- pmd-java/src/main/resources/category/java/codestyle.xml | 4 ++-- .../java/rule/codestyle/UseShortArrayInitializerTest.java | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 9fc7854bef..bc4b73fa6f 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -1836,8 +1836,8 @@ E.g. `int[] x = new int[] { 1, 2, 3 };` can be written as `int[] x = { 1, 2, 3 } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UseShortArrayInitializerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UseShortArrayInitializerTest.java index fe745b690a..be1a80e550 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UseShortArrayInitializerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UseShortArrayInitializerTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseShortArrayInitializerTest extends PmdRuleTst { // no additional unit tests } From 4b543397f66e640ddf701a0ad6b859aebb7d1b8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 17:52:31 +0200 Subject: [PATCH 028/299] Cleanup --- .../java/symbols/internal/UnresolvedClassStore.java | 4 ---- .../java/rule/codestyle/xml/TooManyStaticImports.xml | 10 +++++----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/UnresolvedClassStore.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/UnresolvedClassStore.java index 3f3192e75b..97796c5b73 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/UnresolvedClassStore.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/UnresolvedClassStore.java @@ -69,8 +69,4 @@ public final class UnresolvedClassStore { return makeUnresolvedReference(qualifier.getCanonicalName() + '.' + simpleName, typeArity); } - - public static JClassSymbol untrackedUnresolvedReference(TypeSystem ts, String canonicalName) { - return new FlexibleUnresolvedClassImpl(ts, null, canonicalName); - } } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/TooManyStaticImports.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/TooManyStaticImports.xml index c9fa856aaf..5ba4e8b6a1 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/TooManyStaticImports.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/TooManyStaticImports.xml @@ -8,11 +8,11 @@ simple violation 1 From 3a599e024fff9f6e23fae95c8bcccdc4589c22d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 27 Sep 2020 18:04:10 +0200 Subject: [PATCH 029/299] Update AvoidDollarSigns --- .../rule/codestyle/AvoidDollarSignsRule.java | 41 ------------------- .../resources/category/java/codestyle.xml | 15 ++++++- .../rule/codestyle/AvoidDollarSignsTest.java | 1 - 3 files changed, 14 insertions(+), 43 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidDollarSignsRule.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidDollarSignsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidDollarSignsRule.java deleted file mode 100644 index 84aeb53a1f..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidDollarSignsRule.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.codestyle; - -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -public class AvoidDollarSignsRule extends AbstractJavaRule { - - @Override - public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - if (node.getImage().indexOf('$') != -1) { - addViolation(data, node); - return data; - } - return super.visit(node, data); - } - - @Override - public Object visit(ASTVariableDeclaratorId node, Object data) { - if (node.getImage().indexOf('$') != -1) { - addViolation(data, node); - return data; - } - return super.visit(node, data); - } - - @Override - public Object visit(ASTMethodDeclarator node, Object data) { - if (node.getImage().indexOf('$') != -1) { - addViolation(data, node); - return data; - } - return super.visit(node, data); - } - -} diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index bc4b73fa6f..2e340d3328 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -37,12 +37,25 @@ public class Foo { language="java" since="1.5" message="Avoid using dollar signs in variable/method/class/interface names" - class="net.sourceforge.pmd.lang.java.rule.codestyle.AvoidDollarSignsRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_codestyle.html#avoiddollarsigns"> Avoid using dollar signs in variable/method/class/interface names. 3 + + + + + + + Date: Sun, 27 Sep 2020 18:10:41 +0200 Subject: [PATCH 030/299] Update AvoidProtectedFieldInFinalClass --- .../src/main/resources/category/java/codestyle.xml | 10 +++++----- .../codestyle/AvoidProtectedFieldInFinalClassTest.java | 1 - ...oidProtectedMethodInFinalClassNotExtendingTest.java | 1 - 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 2e340d3328..0c99fd0d7e 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -79,9 +79,9 @@ Clarify your intent by using private or package access modifiers instead. @@ -114,8 +114,8 @@ visibility cannot be reduced). Clarify your intent by using private or package a diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedFieldInFinalClassTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedFieldInFinalClassTest.java index 698650369f..1f22c1d607 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedFieldInFinalClassTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedFieldInFinalClassTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidProtectedFieldInFinalClassTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedMethodInFinalClassNotExtendingTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedMethodInFinalClassNotExtendingTest.java index 3ba8d54d1e..61bb110a08 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedMethodInFinalClassNotExtendingTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidProtectedMethodInFinalClassNotExtendingTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidProtectedMethodInFinalClassNotExtendingTest extends PmdRuleTst { // no additional unit tests } From 9de7298b29879484517855a2d4e422fd7b638e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 21:19:01 +0100 Subject: [PATCH 031/299] Update regression ruleset --- .travis/all-java.xml | 50 ++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 72ed667e22..08be6033c5 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -66,46 +66,46 @@ - - - + + + - - + + - - - + + + - - + + - + - + - - + + - - + + - + - + - - - - + + + + @@ -114,10 +114,10 @@ - - + + - + From d01b6efce77785ba2b5c1e414766868bcaa9d526 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 21:23:03 +0100 Subject: [PATCH 032/299] Update naming conventions rules --- .travis/all-java.xml | 10 ++--- .../AbstractNamingConventionRule.java | 4 +- .../codestyle/ClassNamingConventionsRule.java | 37 ++++++++++++----- .../codestyle/FieldNamingConventionsRule.java | 40 +++++++++++++------ .../FormalParameterNamingConventionsRule.java | 4 ++ .../LocalVariableNamingConventionsRule.java | 6 +++ .../codestyle/ClassNamingConventionsTest.java | 1 - .../codestyle/FieldNamingConventionsTest.java | 1 - .../FormalParameterNamingConventionsTest.java | 1 - .../LocalVariableNamingConventionsTest.java | 1 - .../MethodNamingConventionsTest.java | 1 - 11 files changed, 70 insertions(+), 36 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 08be6033c5..19feb3a6b8 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -72,7 +72,7 @@ - + @@ -82,20 +82,20 @@ - + - + - + - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java index fc1d1e3562..7d8525a0db 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java @@ -49,9 +49,7 @@ abstract class AbstractNamingConventionRule extends Abstract abstract String kindDisplayName(T node, PropertyDescriptor descriptor); /** Extracts the name that should be pattern matched. */ - String nameExtractor(T node) { - return node.getImage(); - } + abstract String nameExtractor(T node); void checkMatches(T node, PropertyDescriptor regex, Object data) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java index b7f547d010..5c5e496d58 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java @@ -6,6 +6,8 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import java.util.regex.Pattern; +import org.checkerframework.checker.nullness.qual.NonNull; + import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTBodyDeclaration; @@ -14,8 +16,13 @@ import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTInitializer; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTRecordDeclaration; import net.sourceforge.pmd.lang.java.ast.AccessNode; +import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility; +import net.sourceforge.pmd.lang.java.ast.JModifier; import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -39,12 +46,15 @@ public class ClassNamingConventionsRule extends AbstractNamingConventionRule descriptor) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsRule.java index 904b7dbdd4..c86eb05705 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsRule.java @@ -4,12 +4,19 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; +import static net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility.V_PUBLIC; +import static net.sourceforge.pmd.lang.java.ast.JModifier.FINAL; +import static net.sourceforge.pmd.lang.java.ast.JModifier.STATIC; + import java.util.List; import java.util.regex.Pattern; +import org.checkerframework.checker.nullness.qual.NonNull; + import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; @@ -46,11 +53,12 @@ public class FieldNamingConventionsRule extends AbstractNamingConventionRule descriptor) { - ASTFieldDeclaration field = (ASTFieldDeclaration) node.getNthParent(2); - if (field.isFinal() && field.isStatic()) { - return field.isPublic() ? "public constant" : "constant"; - } else if (field.isFinal()) { + boolean isFinal = node.hasModifiers(FINAL); + boolean isStatic = node.hasModifiers(STATIC); + + if (isFinal && isStatic) { + return node.getVisibility() == V_PUBLIC ? "public constant" : "constant"; + } else if (isFinal) { return "final field"; - } else if (field.isStatic()) { + } else if (isStatic) { return "static field"; } else { return "field"; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsRule.java index 2ecbfb4a0c..d107b9769c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsRule.java @@ -64,6 +64,10 @@ public final class FormalParameterNamingConventionsRule extends AbstractNamingCo return CAMEL_CASE; } + @Override + String nameExtractor(ASTVariableDeclaratorId node) { + return node.getName(); + } @Override String kindDisplayName(ASTVariableDeclaratorId node, PropertyDescriptor descriptor) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsRule.java index cc4ea66a10..3b0bb8db50 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsRule.java @@ -55,6 +55,12 @@ public final class LocalVariableNamingConventionsRule extends AbstractNamingConv } + @Override + String nameExtractor(ASTVariableDeclaratorId node) { + return node.getName(); + } + + @Override String kindDisplayName(ASTVariableDeclaratorId node, PropertyDescriptor descriptor) { if (node.isExceptionBlockParameter()) { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsTest.java index 6cf69fb473..81605d543f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ClassNamingConventionsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsTest.java index 2141b9496a..c2ab5652a2 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FieldNamingConventionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class FieldNamingConventionsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsTest.java index 5cb25d09b3..11ecb73e9a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/FormalParameterNamingConventionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class FormalParameterNamingConventionsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsTest.java index 23f2a469fc..bee83930e8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/LocalVariableNamingConventionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LocalVariableNamingConventionsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsTest.java index 2af54b09d6..c9f328d06f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/MethodNamingConventionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class MethodNamingConventionsTest extends PmdRuleTst { // no additional unit tests } From a1e24ef7ff9d57a5bf8e6f75c02d952c40ac3407 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 21:38:07 +0100 Subject: [PATCH 033/299] Update Remote(Interface|Session)NamingConvention --- .travis/all-java.xml | 4 ++-- .../resources/category/java/codestyle.xml | 23 ++++--------------- .../RemoteInterfaceNamingConventionTest.java | 1 - ...eSessionInterfaceNamingConventionTest.java | 1 - 4 files changed, 6 insertions(+), 23 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 19feb3a6b8..b3e98883e4 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -100,8 +100,8 @@ - - + + diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 0c99fd0d7e..27e1282eb7 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -1242,17 +1242,8 @@ Remote Interface of a Session EJB should not have a suffix. @@ -1288,14 +1279,8 @@ A Remote Home interface type of a Session EJB should be suffixed by 'Home'. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteInterfaceNamingConventionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteInterfaceNamingConventionTest.java index 57026550ba..1d0c445aea 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteInterfaceNamingConventionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteInterfaceNamingConventionTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class RemoteInterfaceNamingConventionTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteSessionInterfaceNamingConventionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteSessionInterfaceNamingConventionTest.java index 329b25c412..7e95cb5d1a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteSessionInterfaceNamingConventionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/RemoteSessionInterfaceNamingConventionTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class RemoteSessionInterfaceNamingConventionTest extends PmdRuleTst { // no additional unit tests } From 0c6100a0e7b30d2438bb2212c3944b8ddb5e1232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 21:43:30 +0100 Subject: [PATCH 034/299] Update UnnecessaryAnnotationValueElement Convert to XPath rule --- .travis/all-java.xml | 2 +- ...UnnecessaryAnnotationValueElementRule.java | 35 ------------------- .../resources/category/java/codestyle.xml | 12 ++++++- ...UnnecessaryAnnotationValueElementTest.java | 1 - 4 files changed, 12 insertions(+), 38 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryAnnotationValueElementRule.java diff --git a/.travis/all-java.xml b/.travis/all-java.xml index b3e98883e4..58fef437b3 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -106,7 +106,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryAnnotationValueElementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryAnnotationValueElementRule.java deleted file mode 100644 index f1294bd319..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryAnnotationValueElementRule.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.codestyle; - -import java.util.List; - -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTMemberValuePair; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -/** - * @author Kirk Clemens - * @since 6.2.0 - */ -public class UnnecessaryAnnotationValueElementRule extends AbstractJavaRule { - - public UnnecessaryAnnotationValueElementRule() { - addRuleChainVisit(ASTAnnotation.class); - } - - @Override - public Object visit(ASTAnnotation node, Object data) { - - final List annotationProperties = node.findDescendantsOfType(ASTMemberValuePair.class); - // all that needs to be done is check to if there's a single property in the annotation and if if that property is value - // then it's a violation and it should be resolved. - if (annotationProperties.size() == 1 && "value".equals(annotationProperties.get(0).getImage())) { - addViolation(data, node); - } - - return data; - } -} diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 27e1282eb7..25a1c3151f 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -1439,12 +1439,22 @@ import static Yoko; // Too much ! language="java" since="6.2.0" message="Avoid the use of value in annotations when it's the only element" - class="net.sourceforge.pmd.lang.java.rule.codestyle.UnnecessaryAnnotationValueElementRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_codestyle.html#unnecessaryannotationvalueelement"> Avoid the use of value in annotations when it's the only element. 3 + + + + + + + + Date: Tue, 27 Oct 2020 21:49:21 +0100 Subject: [PATCH 035/299] Update AddEmptyString --- pmd-java/src/main/resources/category/java/performance.xml | 2 +- .../pmd/lang/java/rule/performance/AddEmptyStringTest.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 25c7cb9209..8b504bffbe 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -24,7 +24,7 @@ It is much better to use one of the type-specific toString() methods instead. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AddEmptyStringTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AddEmptyStringTest.java index f274a1c79a..c230229435 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AddEmptyStringTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AddEmptyStringTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AddEmptyStringTest extends PmdRuleTst { // no additional unit tests } From c153e27a21721eb3f24cea732be2009b822bf37d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 21:51:26 +0100 Subject: [PATCH 036/299] Update AvoidFileStream --- .travis/all-java.xml | 4 ++-- pmd-java/src/main/resources/category/java/performance.xml | 2 +- .../pmd/lang/java/rule/performance/AvoidFileStreamTest.java | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 58fef437b3..ee0dbd496c 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -285,11 +285,11 @@ - + - + diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 8b504bffbe..61e26e50a4 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -209,7 +209,7 @@ that one covers both. Date: Tue, 27 Oct 2020 22:06:23 +0100 Subject: [PATCH 037/299] Update AvoidUsingShortType --- .travis/all-java.xml | 2 +- .../main/resources/category/java/performance.xml | 14 ++++++-------- .../rule/performance/AvoidUsingShortTypeTest.java | 1 - 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index ee0dbd496c..af6cab60d3 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -291,7 +291,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 61e26e50a4..4996c0f8ce 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -282,17 +282,15 @@ adverse impacts on performance. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidUsingShortTypeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidUsingShortTypeTest.java index ea6421d25c..5a639a8a74 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidUsingShortTypeTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AvoidUsingShortTypeTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidUsingShortTypeTest extends PmdRuleTst { // no additional unit tests } From d80b06600f90bca24ce67f1a97aab62baf254bd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 22:08:55 +0100 Subject: [PATCH 038/299] Update AvoidMessageDigestField --- .travis/all-java.xml | 10 +++++----- .../src/main/resources/category/java/bestpractices.xml | 2 +- .../bestpractices/AvoidMessageDigestFieldTest.java | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index af6cab60d3..c0a6b25947 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -1,9 +1,9 @@ - + Every java rule in PMD which is used for the regression tests with pmdtester @@ -13,7 +13,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 650b24d428..3fff370f9c 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -138,7 +138,7 @@ public class Foo { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidMessageDigestFieldTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidMessageDigestFieldTest.java index 6eac549ace..37d37625e7 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidMessageDigestFieldTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidMessageDigestFieldTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidMessageDigestFieldTest extends PmdRuleTst { // no additional unit tests } From 82c7e3daeb46344c2a6f12303697e63b6daf5e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 22:12:59 +0100 Subject: [PATCH 039/299] Update AvoidPrintStackTrace --- .travis/all-java.xml | 2 +- .../src/main/resources/category/java/bestpractices.xml | 8 ++++---- .../java/rule/bestpractices/AvoidPrintStackTraceTest.java | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index c0a6b25947..4ca470509c 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -14,7 +14,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 3fff370f9c..0f27381340 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -184,10 +184,10 @@ Avoid printStackTrace(); use a logger call instead. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidPrintStackTraceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidPrintStackTraceTest.java index e17a23785b..0a52f862d1 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidPrintStackTraceTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidPrintStackTraceTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidPrintStackTraceTest extends PmdRuleTst { // no additional unit tests } From 390e591949b60408dc0dbb0ee2b5480b9e627f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 22:14:18 +0100 Subject: [PATCH 040/299] Update AvoidStringBufferField --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/bestpractices.xml | 2 +- .../java/rule/bestpractices/AvoidStringBufferFieldTest.java | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 4ca470509c..f501ee87f9 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -18,7 +18,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 0f27381340..e38e2d2d89 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -327,7 +327,7 @@ if held within objects with long lifetimes. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidStringBufferFieldTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidStringBufferFieldTest.java index 87e128b4c2..88219b9257 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidStringBufferFieldTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AvoidStringBufferFieldTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidStringBufferFieldTest extends PmdRuleTst { // no additional unit tests } From 8e8d5d60da214f32cad7da94dfb0512fdeb98cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 22:15:57 +0100 Subject: [PATCH 041/299] Update ConstantsInInterface --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/bestpractices.xml | 2 +- .../lang/java/rule/bestpractices/ConstantsInInterfaceTest.java | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index f501ee87f9..8a3cb35dac 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -21,7 +21,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index e38e2d2d89..878d6d49b1 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -406,7 +406,7 @@ better placed in classes or enums. See Effective Java, item 19. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ConstantsInInterfaceTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ConstantsInInterfaceTest.java index 6e8e65b3a1..e72544c3fd 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ConstantsInInterfaceTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ConstantsInInterfaceTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ConstantsInInterfaceTest extends PmdRuleTst { // no additional unit tests } From a153e0af9a08198806f0187175e491c07bee91da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 22:22:41 +0100 Subject: [PATCH 042/299] Update DefaultLabelNotLastInSwitchStmt --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/bestpractices.xml | 4 +--- .../bestpractices/DefaultLabelNotLastInSwitchStmtTest.java | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 8a3cb35dac..3f30596260 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -22,7 +22,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 878d6d49b1..12c3463e31 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -451,9 +451,7 @@ By convention, the default label should be the last label in a switch statement. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DefaultLabelNotLastInSwitchStmtTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DefaultLabelNotLastInSwitchStmtTest.java index d318d09aaf..7c8d1c663a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DefaultLabelNotLastInSwitchStmtTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DefaultLabelNotLastInSwitchStmtTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DefaultLabelNotLastInSwitchStmtTest extends PmdRuleTst { // no additional unit tests } From d55ea50cc34c43acb03430cc1c598683ff468b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 22:24:18 +0100 Subject: [PATCH 043/299] Update DoubleBraceInitialization --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/bestpractices.xml | 2 +- .../java/rule/bestpractices/DoubleBraceInitializationTest.java | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 3f30596260..566be7cf74 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -23,7 +23,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 12c3463e31..d764c74eb2 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -494,7 +494,7 @@ public class Foo { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DoubleBraceInitializationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DoubleBraceInitializationTest.java index cb88f5816b..2e7b29a342 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DoubleBraceInitializationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/DoubleBraceInitializationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DoubleBraceInitializationTest extends PmdRuleTst { // no additional unit tests } From 2318414a7a0151af85d5333a8b4a42a95df4d461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 22:36:43 +0100 Subject: [PATCH 044/299] Update EmptyCatchBlock Now it doesn't ignore InterruptedException/CloneNotSupportedException --- .travis/all-java.xml | 2 +- docs/pages/7_0_0_release_notes.md | 7 ++++ .../resources/category/java/errorprone.xml | 14 ++++---- .../rule/errorprone/EmptyCatchBlockTest.java | 1 - .../rule/errorprone/xml/EmptyCatchBlock.xml | 33 +++++++++++-------- 5 files changed, 35 insertions(+), 22 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 566be7cf74..c10cbf2b23 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -213,7 +213,7 @@ - + diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index ad8e836afd..c184909755 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -67,6 +67,13 @@ The following previously deprecated rules have been finally removed: * VariableNamingConventions (java-codestyle) * WhileLoopsMustUseBraces (java-codestyle) +#### Changed rules + +##### Java + +* {% rule "java/errorprone/EmptyCatchBlock" %}: `CloneNotSupportedException` and `InterruptedException` are not special-cased anymore. Rename the exception parameter to `ignored` to ignore them. + + ### Fixed Issues * java-bestpractices diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 5121280ffc..9f54835456 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1452,13 +1452,13 @@ or reported. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyCatchBlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyCatchBlockTest.java index f4d2e4661a..53e61e8226 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyCatchBlockTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyCatchBlockTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyCatchBlockTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/EmptyCatchBlock.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/EmptyCatchBlock.xml index a2cee743d1..40006caa25 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/EmptyCatchBlock.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/EmptyCatchBlock.xml @@ -76,26 +76,33 @@ public class Foo { - InterruptedException is OK - 0 + InterruptedException is not OK - changed behavior in PMD7 + 1 + 7 - + public class Foo { + void foo() { + try { + } catch (InterruptedException ignored) {} + try { + } catch (InterruptedException notok) {} + } + } + ]]> + - CloneNotSupportedException is OK - 0 + CloneNotSupportedException is not OK - changed behavior in PMD7 + 1 + 7 From 4aebddf1123ed8b0c8de82acd4a87bad4be5e1ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 22:40:46 +0100 Subject: [PATCH 045/299] Update EmptyFinalizer --- .travis/all-java.xml | 2 +- .../pmd/lang/java/rule/errorprone/EmptyFinalizerTest.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index c10cbf2b23..522058e4c6 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -214,7 +214,7 @@ - + diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinalizerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinalizerTest.java index 66034331c1..bb81c264a3 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinalizerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinalizerTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyFinalizerTest extends PmdRuleTst { // no additional unit tests } From 342fe708c561f1f7906b7f8242fe056b8201885e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 22:42:02 +0100 Subject: [PATCH 046/299] Update EmptyFinallyBlock --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/errorprone.xml | 2 +- .../pmd/lang/java/rule/errorprone/EmptyFinallyBlockTest.java | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 522058e4c6..9da8a42f07 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -215,7 +215,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 9f54835456..8b40d8b4fc 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1521,7 +1521,7 @@ Empty finally blocks serve no purpose and should be removed. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinallyBlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinallyBlockTest.java index 415da04f7c..f076b33017 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinallyBlockTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyFinallyBlockTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyFinallyBlockTest extends PmdRuleTst { // no additional unit tests } From 5aa9a69e9c1e191a223f40b52eb451f3ad8ab887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 22:47:39 +0100 Subject: [PATCH 047/299] Update more empty rules --- .travis/all-java.xml | 16 ++++++++-------- .../resources/category/java/errorprone.xml | 19 ++++++++----------- .../java/rule/errorprone/EmptyIfStmtTest.java | 1 - .../rule/errorprone/EmptyInitializerTest.java | 1 - .../errorprone/EmptyStatementBlockTest.java | 1 - .../EmptyStatementNotInLoopTest.java | 1 - .../errorprone/EmptySwitchStatementsTest.java | 1 - .../EmptySynchronizedBlockTest.java | 1 - .../rule/errorprone/EmptyTryBlockTest.java | 1 - .../rule/errorprone/EmptyWhileStmtTest.java | 1 - 10 files changed, 16 insertions(+), 27 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 9da8a42f07..a5ba8fd965 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -216,14 +216,14 @@ - - - - - - - - + + + + + + + + diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 8b40d8b4fc..56d1a23b6a 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1613,7 +1613,7 @@ Empty block statements serve no purpose and should be removed. 3 - //BlockStatement/Statement/Block[not(*)] + //Block/Block[not(*)] @@ -1650,13 +1650,10 @@ and should be removed. @@ -1714,7 +1711,7 @@ Empty synchronized blocks serve no purpose and should be removed. 3 - //SynchronizedStatement/Block[1][not(*)] + //SynchronizedStatement/Block[not(*)] @@ -1744,7 +1741,7 @@ Avoid empty try blocks - what's the point? @@ -1779,7 +1776,7 @@ a while loop that does a lot in the exit expression, rewrite it to make it clear diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyIfStmtTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyIfStmtTest.java index d5eb20180c..bf2bff63cf 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyIfStmtTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyIfStmtTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyIfStmtTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyInitializerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyInitializerTest.java index 3583c7aa6e..32d1abfab9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyInitializerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyInitializerTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyInitializerTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementBlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementBlockTest.java index 5decf78cbf..2d49e087d9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementBlockTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementBlockTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyStatementBlockTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementNotInLoopTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementNotInLoopTest.java index c41b98d5b5..fe4d05a8e8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementNotInLoopTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyStatementNotInLoopTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyStatementNotInLoopTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySwitchStatementsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySwitchStatementsTest.java index 99c1e3de49..edf5cd3362 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySwitchStatementsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySwitchStatementsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptySwitchStatementsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySynchronizedBlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySynchronizedBlockTest.java index eb7dfb1665..a66b28d808 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySynchronizedBlockTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptySynchronizedBlockTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptySynchronizedBlockTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyTryBlockTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyTryBlockTest.java index b47a75b36d..3c6e1330b6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyTryBlockTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyTryBlockTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyTryBlockTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyWhileStmtTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyWhileStmtTest.java index 6a0488998c..d370081916 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyWhileStmtTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EmptyWhileStmtTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EmptyWhileStmtTest extends PmdRuleTst { // no additional unit tests } From 3249158446eb4eca0a22e50899ed4b10bb52d930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 27 Oct 2020 23:07:08 +0100 Subject: [PATCH 048/299] Fix type test util for anons --- .../pmd/lang/java/types/TypeTestUtil.java | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index c538b66d66..e13fdb72a4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -17,6 +17,7 @@ import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; import net.sourceforge.pmd.lang.java.symbols.internal.UnresolvedClassStore; +import net.sourceforge.pmd.util.OptionalBool; /** * Public utilities to test the type of nodes. @@ -106,8 +107,10 @@ public final class TypeTestUtil { AssertionUtil.requireParamNotNull("canonicalName", (Object) canonicalName); if (node == null) { return false; - } else if (isExactlyA(canonicalName, node)) { - return true; + } + OptionalBool exactMatch = isExactlyAOrAnon(canonicalName, node); + if (exactMatch != OptionalBool.NO) { + return exactMatch == OptionalBool.YES; // otherwise anon, and we return false } JTypeMirror thisType = node.getTypeMirror(); @@ -125,13 +128,19 @@ public final class TypeTestUtil { TypeSystem ts = thisType.getTypeSystem(); UnresolvedClassStore unresolvedStore = InternalApiBridge.getProcessor(node).getUnresolvedStore(); @Nullable JTypeMirror otherType = TypesFromReflection.loadType(ts, canonicalName, unresolvedStore); - if (otherType == null) { + if (otherType == null + || otherType.isClassOrInterface() && ((JClassType) otherType).getSymbol().isAnonymousClass()) { return false; // we know isExactlyA(canonicalName, node); returned false } return thisType.isSubtypeOf(otherType); } + private static boolean isAnonymous(JTypeMirror type) { + JTypeDeclSymbol symbol = type.getSymbol(); + return symbol instanceof JClassSymbol && ((JClassSymbol) symbol).isAnonymousClass(); + } + private static boolean isAnnotationSuperType(String clazzName) { // then, the supertype may only be Object, j.l.Annotation // this is used e.g. by the typeIs function in XPath @@ -210,25 +219,31 @@ public final class TypeTestUtil { * @throws NullPointerException if the class name parameter is null */ public static boolean isExactlyA(@NonNull String canonicalName, final @Nullable TypeNode node) { + return isExactlyAOrAnon(canonicalName, node) == OptionalBool.YES; + } + + private static OptionalBool isExactlyAOrAnon(@NonNull String canonicalName, final @Nullable TypeNode node) { AssertionUtil.requireParamNotNull("canonicalName", canonicalName); if (node == null) { - return false; + return OptionalBool.NO; } JTypeDeclSymbol sym = node.getTypeMirror().getSymbol(); if (sym == null || sym instanceof JTypeParameterSymbol) { - return false; + return OptionalBool.NO; } canonicalName = StringUtils.deleteWhitespace(canonicalName); JClassSymbol klass = (JClassSymbol) sym; String canonical = klass.getCanonicalName(); - return canonical != null && canonical.equals(canonicalName); + if (canonical == null) { + return OptionalBool.UNKNOWN; // anonymous + } + return OptionalBool.definitely(canonical.equals(canonicalName)); } - private static boolean hasNoSubtypes(Class clazz) { // Neither final nor an annotation. Enums & records have ACC_FINAL // Note: arrays have ACC_FINAL, but have subtypes by covariance From c18a04572dbc206d960bcba458e78d911c38245e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 28 Oct 2020 09:05:38 +0100 Subject: [PATCH 049/299] Checkstyle --- .../pmd/lang/java/rule/xpath/internal/GetModifiersFun.java | 2 +- .../net/sourceforge/pmd/lang/java/types/TypeTestUtil.java | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFun.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFun.java index 961e75c96f..d9713ba452 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFun.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFun.java @@ -29,7 +29,7 @@ import net.sf.saxon.value.StringValue; * *

Returns true if the type of the node matches, false otherwise. */ -public class GetModifiersFun extends BaseJavaXPathFunction { +public final class GetModifiersFun extends BaseJavaXPathFunction { private static final SequenceType[] ARGTYPES = {}; private final boolean explicit; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index e13fdb72a4..98a8d1d0ff 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -136,11 +136,6 @@ public final class TypeTestUtil { return thisType.isSubtypeOf(otherType); } - private static boolean isAnonymous(JTypeMirror type) { - JTypeDeclSymbol symbol = type.getSymbol(); - return symbol instanceof JClassSymbol && ((JClassSymbol) symbol).isAnonymousClass(); - } - private static boolean isAnnotationSuperType(String clazzName) { // then, the supertype may only be Object, j.l.Annotation // this is used e.g. by the typeIs function in XPath From 8edd4b61c47b2d681a5f6600fc3338170a43efb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 28 Oct 2020 09:17:33 +0100 Subject: [PATCH 050/299] Update UncommentedEmptyConstructor --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/documentation.xml | 4 ++-- .../rule/documentation/UncommentedEmptyConstructorTest.java | 4 ---- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index a5ba8fd965..98b1c655bd 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -168,7 +168,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/documentation.xml b/pmd-java/src/main/resources/category/java/documentation.xml index ccdc5a3301..babf7d5784 100644 --- a/pmd-java/src/main/resources/category/java/documentation.xml +++ b/pmd-java/src/main/resources/category/java/documentation.xml @@ -96,8 +96,8 @@ and unintentional empty constructors. Date: Wed, 28 Oct 2020 09:37:05 +0100 Subject: [PATCH 051/299] Add doc for last functions --- docs/_data/xpath_funs.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/_data/xpath_funs.yml b/docs/_data/xpath_funs.yml index 80c012fe51..ec60c77754 100644 --- a/docs/_data/xpath_funs.yml +++ b/docs/_data/xpath_funs.yml @@ -84,3 +84,40 @@ langs: examples: - code: '//MethodDeclaration[pmd-java:hasAnnotation("java.lang.Override")]' outcome: "Matches all method declarations that are annotated with @Override" + + - name: modifiers + returnType: "xs:string*" + shortDescription: "Produce the effective modifiers of a node" + description: >- + Returns a sequence of the effective modifiers of a node as strings. + This is documented on {% jdoc jast::ASTModifierList#getEffectiveModifiers() %}. + + notes: "The context node must be an {% jdoc jast::AccessNode %}" + parameters: + examples: + - code: '//MethodDeclaration[pmd-java:modifiers() = "native"]' + outcome: "Matches native method declarations" + - code: '//MethodDeclaration[pmd-java:modifiers() = ("native", "static")]' + outcome: >- + Matches method declarations that have a 'native' OR a 'static' modifier. + This may be counter-intuitive. + - code: '//MethodDeclaration[pmd-java:modifiers() = "public"]' + outcome: >- + Matches method declarations that have a 'public' modifier, explicit or implicit. + For example, this would match methods in interfaces, which implicitly have the + modifier. Use the `explicitModifiers` function if you don't want the implicit part. + Also note that `@Visibility = 'public'` is a better use of the API, in this particular + instance. + + - name: explicitModifiers + returnType: "xs:string*" + shortDescription: "Produce the explicit modifiers of a node" + description: >- + Returns a sequence of the explicit modifiers of a node as strings. + This is documented on {% jdoc jast::ASTModifierList#getExplicitModifiers() %}. + + notes: "The context node must be an {% jdoc jast::AccessNode %}" + parameters: + examples: + - code: '//MethodDeclaration[pmd-java:explicitModifiers() = "public"]' + outcome: "Matches method declarations that have an explicit 'public' modifier." From c89256ac97ff8d3821c00aaf4bd1fea0cf33083f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 28 Oct 2020 10:24:54 +0100 Subject: [PATCH 052/299] Optimise node is function --- docs/_data/xpath_funs.yml | 4 +- .../java/internal/JavaLanguageHandler.java | 3 +- .../internal/BaseContextNodeTestFun.java | 16 +--- .../rule/xpath/internal/MetricFunction.java | 6 -- .../rule/xpath/internal/NodeIsFunction.java | 84 +++++++++++++++++++ 5 files changed, 91 insertions(+), 22 deletions(-) create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunction.java diff --git a/docs/_data/xpath_funs.yml b/docs/_data/xpath_funs.yml index ec60c77754..4780f462a9 100644 --- a/docs/_data/xpath_funs.yml +++ b/docs/_data/xpath_funs.yml @@ -26,9 +26,9 @@ langs: description: "Simple name of a class or interface in package {% jdoc_package :jast %}, without the 'AST' prefix" examples: - code: '//*[pmd-java:nodeIs("Expression")]' - outcome: "Matches all nodes that implement ASTExpression" + outcome: "Matches all nodes that implement {% jdoc jast::ASTExpression %}" - code: '//*[pmd-java:nodeIs("AnyTypeDeclaration")]' - outcome: "Matches all nodes that implement ASTAnyTypeDeclaration" + outcome: "Matches all nodes that implement {% jdoc jast::ASTAnyTypeDeclaration %}" - code: '//*[pmd-java:nodeIs("Foo")]' outcome: "Runtime error, there's no class ASTFoo in the package" diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java index 14567972b2..b5fd9ec0b5 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java @@ -22,6 +22,7 @@ import net.sourceforge.pmd.lang.java.rule.xpath.internal.BaseContextNodeTestFun; import net.sourceforge.pmd.lang.java.rule.xpath.internal.GetCommentOnFunction; import net.sourceforge.pmd.lang.java.rule.xpath.internal.GetModifiersFun; import net.sourceforge.pmd.lang.java.rule.xpath.internal.MetricFunction; +import net.sourceforge.pmd.lang.java.rule.xpath.internal.NodeIsFunction; import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; import net.sourceforge.pmd.lang.metrics.MetricKey; import net.sourceforge.pmd.lang.metrics.internal.AbstractLanguageMetricsProvider; @@ -36,7 +37,7 @@ public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler { BaseContextNodeTestFun.TYPE_IS_EXACTLY, BaseContextNodeTestFun.TYPE_IS, BaseContextNodeTestFun.HAS_ANNOTATION, - BaseContextNodeTestFun.NODE_IS, + NodeIsFunction.INSTANCE, GetModifiersFun.GET_EFFECTIVE, GetModifiersFun.GET_EXPLICIT, MetricFunction.INSTANCE, diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java index c41bfa422e..e7d41d0b17 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java @@ -30,23 +30,13 @@ import net.sf.saxon.value.SequenceType; */ public class BaseContextNodeTestFun extends BaseJavaXPathFunction { - private static final SequenceType[] ARGTYPES = {SequenceType.SINGLE_STRING}; + static final SequenceType[] SINGLE_STRING_SEQ = {SequenceType.SINGLE_STRING}; private final Class klass; private final BiPredicate checker; public static final BaseJavaXPathFunction TYPE_IS_EXACTLY = new BaseContextNodeTestFun<>(TypeNode.class, "typeIsExactly", TypeTestUtil::isExactlyA); public static final BaseJavaXPathFunction TYPE_IS = new BaseContextNodeTestFun<>(TypeNode.class, "typeIs", TypeTestUtil::isA); public static final BaseJavaXPathFunction HAS_ANNOTATION = new BaseContextNodeTestFun<>(Annotatable.class, "hasAnnotation", (name, node) -> node.isAnnotationPresent(name)); - public static final BaseJavaXPathFunction NODE_IS = - new BaseContextNodeTestFun<>(JavaNode.class, "nodeIs", (name, node) -> { - Class klass; - try { - klass = Class.forName("net.sourceforge.pmd.lang.java.ast.AST" + name); - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException("No class AST" + name); - } - return klass.isInstance(node); - }); protected BaseContextNodeTestFun(Class klass, String localName, BiPredicate checker) { super(localName); @@ -56,7 +46,7 @@ public class BaseContextNodeTestFun extends BaseJavaXPathFun @Override public SequenceType[] getArgumentTypes() { - return ARGTYPES; + return SINGLE_STRING_SEQ; } @Override @@ -81,7 +71,7 @@ public class BaseContextNodeTestFun extends BaseJavaXPathFun if (klass.isInstance(contextNode)) { return BooleanValue.get(checker.test(fullTypeName, (T) contextNode)); } else { - throw new IllegalArgumentException( + throw new XPathException( getFunctionQName().getLocalPart() + " function may only be called on an instance of " + klass.getSimpleName()); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MetricFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MetricFunction.java index 6337d147ea..8b784b6473 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MetricFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MetricFunction.java @@ -18,8 +18,6 @@ import net.sourceforge.pmd.lang.metrics.MetricKey; import net.sourceforge.pmd.lang.metrics.MetricsUtil; import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode; -import net.sf.saxon.expr.Expression; -import net.sf.saxon.expr.StaticContext; import net.sf.saxon.expr.XPathContext; import net.sf.saxon.lib.ExtensionFunctionCall; import net.sf.saxon.om.Sequence; @@ -69,10 +67,6 @@ public final class MetricFunction extends BaseJavaXPathFunction { @Override public ExtensionFunctionCall makeCallExpression() { return new ExtensionFunctionCall() { - @Override - public Expression rewrite(StaticContext context, Expression[] arguments) throws XPathException { - return super.rewrite(context, arguments); - } @Override public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunction.java new file mode 100644 index 0000000000..c6d9ac74b0 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunction.java @@ -0,0 +1,84 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.xpath.internal; + +import static net.sourceforge.pmd.lang.java.rule.xpath.internal.BaseContextNodeTestFun.SINGLE_STRING_SEQ; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode; + +import net.sf.saxon.expr.Expression; +import net.sf.saxon.expr.StaticContext; +import net.sf.saxon.expr.StringLiteral; +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.lib.ExtensionFunctionCall; +import net.sf.saxon.om.Sequence; +import net.sf.saxon.trans.XPathException; +import net.sf.saxon.value.BooleanValue; +import net.sf.saxon.value.SequenceType; + +public final class NodeIsFunction extends BaseJavaXPathFunction { + + public static final NodeIsFunction INSTANCE = new NodeIsFunction(); + + private NodeIsFunction() { + super("nodeIs"); + } + + @Override + public SequenceType[] getArgumentTypes() { + return SINGLE_STRING_SEQ; + } + + @Override + public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { + return SequenceType.SINGLE_BOOLEAN; + } + + @Override + public boolean dependsOnFocus() { + return true; + } + + @Override + public ExtensionFunctionCall makeCallExpression() { + return new ExtensionFunctionCall() { + + private Class constantTestType = null; + + @Override + public Expression rewrite(StaticContext context, Expression[] arguments) throws XPathException { + // If the argument is a string literal then we can preload + // the class, and check that it's valid at expression build time + + Expression firstArg = arguments[0]; // this expression has been type checked so there is an argument + if (firstArg instanceof StringLiteral) { + String name = ((StringLiteral) firstArg).getStringValue(); + constantTestType = getClassFromArg(name); + } + return null; + } + + @Override + public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { + Node node = ((AstElementNode) context.getContextItem()).getUnderlyingNode(); + Class klass = constantTestType != null ? constantTestType + : getClassFromArg(arguments[0].head().getStringValue()); + return BooleanValue.get(klass.isInstance(node)); + } + }; + } + + private static Class getClassFromArg(String name) throws XPathException { + Class klass; + try { + klass = Class.forName("net.sourceforge.pmd.lang.java.ast.AST" + name); + } catch (ClassNotFoundException e) { + throw new XPathException("No class named AST" + name); + } + return klass; + } + +} From 9522bda9d9ef94df6466373d363c24497f392fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 28 Oct 2020 19:35:26 +0100 Subject: [PATCH 053/299] Improve AvoidUsingShortType perf --- .../main/resources/category/java/performance.xml | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 4996c0f8ce..f3ce4f9296 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -282,15 +282,12 @@ adverse impacts on performance. From 949a07639ad4eaa6bd24a12509027c6125fbf889 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 28 Oct 2020 19:36:22 +0100 Subject: [PATCH 054/299] Use rulechain for MethodNamingConventions --- .../java/rule/codestyle/MethodNamingConventionsRule.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 f921900586..4ef1008e24 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 @@ -10,6 +10,8 @@ import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; +import org.checkerframework.checker.nullness.qual.NonNull; + import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; @@ -17,6 +19,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; import net.sourceforge.pmd.properties.PropertyBuilder.RegexPropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -42,6 +45,11 @@ public class MethodNamingConventionsRule extends AbstractNamingConventionRule Date: Thu, 29 Oct 2020 21:09:40 +0100 Subject: [PATCH 055/299] Make AvoidDollarSigns use rulechain --- .../src/main/resources/category/java/codestyle.xml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 25a1c3151f..e0495c5c8c 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -47,10 +47,12 @@ Avoid using dollar signs in variable/method/class/interface names. From ecc95d6fd31764d3d2d4e855176862c3d49a9229 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 21:49:45 +0100 Subject: [PATCH 056/299] Fix AvoidUsingShortType --- pmd-java/src/main/resources/category/java/performance.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index f3ce4f9296..f06e7d8d93 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -285,7 +285,7 @@ adverse impacts on performance. //PrimitiveType[@Kind = 'short'][ parent::FieldDeclaration or parent::LocalVariableDeclaration - or parent::FormalParameter and ../../self::MethodDeclaration[not(pmd-java:hasAnnotation('java.lang.Override'))] + or parent::FormalParameter and ../../../self::MethodDeclaration[not(pmd-java:hasAnnotation('java.lang.Override'))] or parent::MethodDeclaration[not(pmd-java:hasAnnotation('java.lang.Override'))] ] ]]> From 381c5b700900f6a08a97a7e5f2f70ba077bd42a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 22:30:24 +0100 Subject: [PATCH 057/299] Update performance.xml/*Instantiation --- .travis/all-java.xml | 8 ++++---- .../main/resources/category/java/performance.xml | 16 ++++------------ .../rule/performance/ByteInstantiationTest.java | 1 - .../performance/IntegerInstantiationTest.java | 1 - .../rule/performance/LongInstantiationTest.java | 1 - .../rule/performance/ShortInstantiationTest.java | 1 - 6 files changed, 8 insertions(+), 20 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 98b1c655bd..d079e6dda6 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -294,17 +294,17 @@ - + - - + + - + diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index f06e7d8d93..b69d020fb6 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -363,9 +363,7 @@ Note that new Byte() is deprecated since JDK 9 for that reason. @@ -539,9 +537,7 @@ Note that new Integer() is deprecated since JDK 9 for that reason. @@ -571,9 +567,7 @@ Note that new Long() is deprecated since JDK 9 for that reason. @@ -735,9 +729,7 @@ Note that new Short() is deprecated since JDK 9 for that reason. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ByteInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ByteInstantiationTest.java index 4dbc1dac69..2db2199779 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ByteInstantiationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ByteInstantiationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ByteInstantiationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/IntegerInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/IntegerInstantiationTest.java index 0b874c069a..a951c8eda7 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/IntegerInstantiationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/IntegerInstantiationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class IntegerInstantiationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/LongInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/LongInstantiationTest.java index c21a845ad7..a2b951cb00 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/LongInstantiationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/LongInstantiationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LongInstantiationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ShortInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ShortInstantiationTest.java index 6516321a80..564c94b6fd 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ShortInstantiationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/ShortInstantiationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ShortInstantiationTest extends PmdRuleTst { // no additional unit tests } From 67097d221a0326fe5782d5cb8f383aa7d5af626a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 22:41:44 +0100 Subject: [PATCH 058/299] Update UselessStringValueOf --- .travis/all-java.xml | 2 +- .../performance/UselessStringValueOfRule.java | 120 +++++++----------- .../pmd/lang/java/types/TypeTestUtil.java | 2 +- .../performance/UselessStringValueOfTest.java | 1 - .../performance/xml/UselessStringValueOf.xml | 32 ++++- 5 files changed, 77 insertions(+), 80 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index d079e6dda6..5f71f5b8c2 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -316,7 +316,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java index db64609724..7ae956357b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java @@ -4,91 +4,59 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression; -import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTReferenceType; -import net.sourceforge.pmd.lang.java.ast.ASTType; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.BinaryOp; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; -import net.sourceforge.pmd.lang.symboltable.NameDeclaration; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; public class UselessStringValueOfRule extends AbstractJavaRule { + @Override - public Object visit(ASTPrimaryPrefix node, Object data) { - if (node.getNumChildren() == 0 || !(node.getChild(0) instanceof ASTName)) { - return super.visit(node, data); - } - - String image = ((ASTName) node.getChild(0)).getImage(); - - if ("String.valueOf".equals(image)) { - Node parent = node.getParent(); - if (parent.getNumChildren() != 2) { - return super.visit(node, data); - } - // skip String.valueOf(anyarraytype[]) - ASTArgumentList args = parent.getFirstDescendantOfType(ASTArgumentList.class); - if (args != null) { - ASTName arg = args.getFirstDescendantOfType(ASTName.class); - if (arg != null) { - NameDeclaration declaration = arg.getNameDeclaration(); - if (declaration != null) { - ASTType argType = declaration.getNode().getParent().getParent() - .getFirstDescendantOfType(ASTType.class); - if (argType != null && argType.getChild(0) instanceof ASTReferenceType - // FIXME - REVERT ME - // && ((ASTReferenceType) argType.getChild(0)).isArray() - ) { - return super.visit(node, data); - } - } - } - } - - Node gp = parent.getParent(); - if (parent instanceof ASTPrimaryExpression && gp instanceof ASTAdditiveExpression - && "+".equals(gp.getImage())) { - boolean ok = false; - if (gp.getChild(0) == parent) { - ok = !isPrimitive(gp.getChild(1)); - } else { - for (int i = 0; !ok && gp.getChild(i) != parent; i++) { - ok = !isPrimitive(gp.getChild(i)); - } - } - if (ok) { - super.addViolation(data, node); - return data; - } - } - } - return super.visit(node, data); + protected @NonNull RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(ASTMethodCall.class); } - private static boolean isPrimitive(Node parent) { - boolean result = false; - if (parent instanceof ASTPrimaryExpression && parent.getNumChildren() == 1) { - Node child = parent.getChild(0); - if (child instanceof ASTPrimaryPrefix && child.getNumChildren() == 1) { - Node gc = child.getChild(0); - if (gc instanceof ASTName) { - ASTName name = (ASTName) gc; - NameDeclaration nd = name.getNameDeclaration(); - if (nd instanceof VariableNameDeclaration && ((VariableNameDeclaration) nd).isPrimitiveType()) { - result = true; - } - } else if (gc instanceof ASTLiteral) { - result = !((ASTLiteral) gc).isStringLiteral(); - } + @Override + public Object visit(ASTMethodCall node, Object data) { + if (node.getParent() instanceof ASTInfixExpression + && ((ASTInfixExpression) node.getParent()).getOperator() == BinaryOp.ADD) { + ASTExpression valueOfArg = getValueOfArg(node); + if (valueOfArg == null) { + return data; //not a valueOf call + } else if (TypeTestUtil.isExactlyA(String.class, valueOfArg)) { + addViolation(data, node); // valueOf call on a string + return data; + } + + ASTExpression sibling = (ASTExpression) node.getParent().getChild(1 - node.getIndexInParent()); + if (TypeTestUtil.isExactlyA(String.class, sibling) + && !valueOfArg.getTypeMirror().isArray() + // In `String.valueOf(a) + String.valueOf(b)`, + // only report the second call + && (getValueOfArg(sibling) == null || node.getIndexInParent() == 1)) { + addViolation(data, node); } } - return result; + return data; + } + + private static @Nullable ASTExpression getValueOfArg(ASTExpression expr) { + if (expr instanceof ASTMethodCall) { + ASTMethodCall call = (ASTMethodCall) expr; + if (call.getArguments().size() == 1 + && call.getMethodName().equals("valueOf") + && TypeTestUtil.isExactlyA(String.class, call.getMethodType().getDeclaringType().getSymbol())) { + return call.getArguments().get(0); + } + } + return null; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index 98a8d1d0ff..341a39fd9c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -173,7 +173,7 @@ public final class TypeTestUtil { return isExactlyA(clazz, node.getTypeMirror().getSymbol()); } - private static boolean isExactlyA(@NonNull Class klass, @Nullable JTypeDeclSymbol type) { + public static boolean isExactlyA(@NonNull Class klass, @Nullable JTypeDeclSymbol type) { AssertionUtil.requireParamNotNull("klass", klass); if (!(type instanceof JClassSymbol)) { // Class cannot reference a type parameter diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfTest.java index bb24f281d4..ef7221eafa 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UselessStringValueOfTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UselessStringValueOf.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UselessStringValueOf.xml index 538b0cb9e0..66393c8379 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UselessStringValueOf.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UselessStringValueOf.xml @@ -45,10 +45,12 @@ public class Foo { valueOf as first/last expression in concatenation 1 + 4 @@ -66,6 +68,34 @@ public class Foo { ]]> + + Double valueOf call, only second is reported + 1 + 4 + + + + + valueOf a string (both are reported) + 2 + 3,4 + + + [ 1977438 ] False positive for UselessStringValueOf 0 From a7f5111a3b2577a6619b65cfe834cfe3b46f8130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 22:55:13 +0100 Subject: [PATCH 059/299] Update UseIndexOfChar rule --- .travis/all-java.xml | 2 +- .../java/rule/AbstractPoorMethodCall.java | 123 ------------------ .../rule/performance/UseIndexOfCharRule.java | 48 +++---- .../rule/performance/UseIndexOfCharTest.java | 1 - 4 files changed, 21 insertions(+), 153 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractPoorMethodCall.java diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 5f71f5b8c2..15745a7bae 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -313,7 +313,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractPoorMethodCall.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractPoorMethodCall.java deleted file mode 100644 index b869265b19..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractPoorMethodCall.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule; - -import java.util.List; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; - -/** - * Detects and flags the occurrences of specific method calls against an - * instance of a designated class. I.e. String.indexOf. The goal is to be able - * to suggest more efficient/modern ways of implementing the same function. - * - *

Concrete subclasses are expected to provide the name of the target class and - * an array of method names that we are looking for. We then pass judgment on - * any literal arguments we find in the subclass as well. - * - * @author Brian Remedios - * @deprecated Internal API - */ -@Deprecated -@InternalApi -public abstract class AbstractPoorMethodCall extends AbstractJavaRule { - // FIXME not sure the abstraction is generic enough to be reused as is. - - /** - * The name of the type the method will be invoked against. - * - * @return String - */ - protected abstract String targetTypename(); - - /** - * Return the names of all the methods we are scanning for, no brackets or - * argument types. - * - * @return String[] - */ - protected abstract String[] methodNames(); - - /** - * Returns whether the node being sent to the method is OK or not. Return - * true if you want to record the method call as a violation. - * - * @param arg - * the node to inspect - * @return boolean - */ - protected abstract boolean isViolationArgument(Node arg); - - /** - * Returns whether the name occurrence is one of the method calls we are - * interested in. - * - * @param occurrence - * NameOccurrence - * @return boolean - */ - private boolean isNotedMethod(NameOccurrence occurrence) { - - if (occurrence == null) { - return false; - } - - String methodCall = occurrence.getImage(); - String[] methodNames = methodNames(); - - for (String element : methodNames) { - if (methodCall.contains(element)) { - return true; - } - } - return false; - } - - /** - * Method visit. - * - * @param node - * ASTVariableDeclaratorId - * @param data - * Object - * @return Object - * @see net.sourceforge.pmd.lang.java.ast.JavaParserVisitor#visit(ASTVariableDeclaratorId, - * Object) - */ - @Override - public Object visit(ASTVariableDeclaratorId node, Object data) { - if (node.getNameDeclaration() == null || !targetTypename().equals(node.getNameDeclaration().getTypeImage())) { - return data; - } - - for (NameOccurrence occ : node.getUsages()) { - JavaNameOccurrence jocc = (JavaNameOccurrence) occ; - if (isNotedMethod(jocc.getNameForWhichThisIsAQualifier())) { - Node parent = jocc.getLocation().getParent().getParent(); - if (parent instanceof ASTPrimaryExpression) { - // bail out if it's something like indexOf("a" + "b") - if (parent.hasDescendantOfType(ASTAdditiveExpression.class)) { - return data; - } - List literals = parent.findDescendantsOfType(ASTLiteral.class); - for (int l = 0; l < literals.size(); l++) { - ASTLiteral literal = literals.get(l); - if (isViolationArgument(literal)) { - addViolation(data, jocc.getLocation()); - } - } - } - } - } - return data; - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharRule.java index e2d9c9ddd1..6a335069c9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharRule.java @@ -4,41 +4,33 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.rule.AbstractPoorMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** + * */ -public class UseIndexOfCharRule extends AbstractPoorMethodCall { +public class UseIndexOfCharRule extends AbstractJavaRulechainRule { - private static final String TARGET_TYPE_NAME = "String"; - private static final String[] METHOD_NAMES = new String[] { "indexOf", "lastIndexOf" }; - - /** - * Method targetTypeName. - * - * @return String - */ - @Override - protected String targetTypename() { - return TARGET_TYPE_NAME; - } - - /** - * Method methodNames. - * - * @return String[] - */ - @Override - protected String[] methodNames() { - return METHOD_NAMES; + public UseIndexOfCharRule() { + super(ASTMethodCall.class); } @Override - protected boolean isViolationArgument(Node arg) { - return true; - // FIXME - REVERT ME - // return ((ASTLiteral) arg).isSingleCharacterStringLiteral(); + public Object visit(ASTMethodCall node, Object data) { + if (node.getMethodName().equals("indexOf") || node.getMethodName().equals("lastIndexOf")) { + if (TypeTestUtil.isA(String.class, node.getQualifier()) + && node.getArguments().size() >= 1) { // there are two overloads of each + ASTExpression arg = node.getArguments().get(0); + if (arg instanceof ASTStringLiteral && ((ASTStringLiteral) arg).getConstValue().length() == 1) { + addViolation(data, node); + } + } + } + return data; } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharTest.java index b79726c7d2..401a76f34b 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseIndexOfCharTest extends PmdRuleTst { // no additional unit tests } From 8b8d5497d72c54d2a98ce62c80c7b8afb4bd0ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 23:11:09 +0100 Subject: [PATCH 060/299] Update RedundantFieldInitializer --- .travis/all-java.xml | 2 +- .../RedundantFieldInitializerRule.java | 89 ++++++------------- .../RedundantFieldInitializerTest.java | 1 - 3 files changed, 28 insertions(+), 64 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 15745a7bae..cd3188c7f6 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -303,7 +303,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerRule.java index 86e6ff187c..d5670f13a6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerRule.java @@ -4,18 +4,18 @@ package net.sourceforge.pmd.lang.java.rule.performance; +import org.checkerframework.checker.nullness.qual.NonNull; + import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTCastExpression; -import net.sourceforge.pmd.lang.java.ast.ASTCharLiteral; 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.ASTNullLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; +import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.ast.JModifier; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind; +import net.sourceforge.pmd.lang.java.types.JTypeMirror; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; /** * Detects redundant field initializers, i.e. the field initializer expressions @@ -26,73 +26,38 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; */ public class RedundantFieldInitializerRule extends AbstractJavaRule { - public RedundantFieldInitializerRule() { - addRuleChainVisit(ASTFieldDeclaration.class); + + @Override + protected @NonNull RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(ASTFieldDeclaration.class); } @Override public Object visit(ASTFieldDeclaration fieldDeclaration, Object data) { - if (declaresNotFinalField(fieldDeclaration)) { - for (ASTVariableDeclarator varDecl : fieldDeclaration.descendants(ASTVariableDeclarator.class)) { - if (hasRedundantInitializer(fieldDeclaration, varDecl)) { - addViolation(data, varDecl); + if (!fieldDeclaration.hasModifiers(JModifier.FINAL)) { + for (ASTVariableDeclaratorId varId : fieldDeclaration.getVarIds()) { + ASTExpression init = varId.getInitializer(); + if (init != null) { + if (isDefaultValue(varId.getTypeMirror(), init)) { + addViolation(data, varId); + } } } } return data; } - private boolean declaresNotFinalField(ASTFieldDeclaration fieldDeclaration) { - return !fieldDeclaration.hasModifiers(JModifier.FINAL); - } - - private boolean hasRedundantInitializer(ASTFieldDeclaration fieldDeclaration, ASTVariableDeclarator varDecl) { - return declaresFieldOfPrimitiveType(fieldDeclaration) - && hasRedundantInitializerOfPrimitive(varDecl) - || hasRedundantInitializerOfReference(varDecl); - } - - private boolean declaresFieldOfPrimitiveType(ASTFieldDeclaration fieldDeclaration) { - return fieldDeclaration.getTypeNode() instanceof ASTPrimitiveType; - } - - private boolean hasRedundantInitializerOfPrimitive(ASTVariableDeclarator varDecl) { - ASTLiteral literal = getLiteralValue(varDecl.getInitializer()); - if (literal != null) { - if (literal instanceof ASTNumericLiteral) { - return hasDefaultNumericValue((ASTNumericLiteral) literal); - } else if (literal instanceof ASTCharLiteral) { - return hasDefaultCharLiteralValue((ASTCharLiteral) literal); - } else if (literal instanceof ASTBooleanLiteral) { - return isDefaultBooleanLiteral((ASTBooleanLiteral) literal); + private boolean isDefaultValue(JTypeMirror type, ASTExpression expr) { + if (type.isPrimitive()) { + if (type.isPrimitive(PrimitiveTypeKind.BOOLEAN)) { + return expr instanceof ASTBooleanLiteral && !((ASTBooleanLiteral) expr).isTrue(); + } else { + Object constValue = expr.getConstValue(); + return constValue instanceof Number && ((Number) constValue).doubleValue() == 0d + || constValue instanceof Character && constValue.equals('\u0000'); } + } else { + return expr instanceof ASTNullLiteral; } - return false; } - - private boolean hasDefaultNumericValue(ASTNumericLiteral literal) { - return literal.getConstValue().doubleValue() == 0; - } - - private boolean hasDefaultCharLiteralValue(ASTCharLiteral literal) { - return literal.getConstValue() == '\0'; - } - - private boolean isDefaultBooleanLiteral(ASTBooleanLiteral literal) { - return !literal.isTrue(); - } - - private boolean hasRedundantInitializerOfReference(ASTVariableDeclarator varDecl) { - return getLiteralValue(varDecl.getInitializer()) instanceof ASTNullLiteral; - } - - private ASTLiteral getLiteralValue(ASTExpression expr) { - if (expr instanceof ASTLiteral) { - return (ASTLiteral) expr; - } else if (expr instanceof ASTCastExpression) { - return getLiteralValue(((ASTCastExpression) expr).getOperand()); - } - return null; - } - } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerTest.java index 263ecd0b79..0a3548a3f6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class RedundantFieldInitializerTest extends PmdRuleTst { // no additional unit tests } From a5410e8750097a09080649bc70ba5532d19a2a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 23:19:48 +0100 Subject: [PATCH 061/299] Update AppendCharacterWithChar --- .travis/all-java.xml | 2 +- .../AppendCharacterWithCharRule.java | 56 +++++++++---------- .../performance/UselessStringValueOfRule.java | 2 +- .../pmd/lang/java/types/TypeTestUtil.java | 13 +++++ .../AppendCharacterWithCharTest.java | 1 - 5 files changed, 40 insertions(+), 34 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index cd3188c7f6..ebf95cdf4a 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -286,7 +286,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharRule.java index c4cb425c5f..5e2575f681 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharRule.java @@ -4,8 +4,15 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; +import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; /** * This rule finds the following: @@ -22,39 +29,26 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; */ public class AppendCharacterWithCharRule extends AbstractJavaRule { - public AppendCharacterWithCharRule() { - addRuleChainVisit(ASTLiteral.class); + @Override + protected @NonNull RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(ASTStringLiteral.class); } @Override - public Object visit(ASTLiteral node, Object data) { - // REVERT ME - // ASTBlockStatement bs = node.getFirstParentOfType(ASTBlockStatement.class); - // if (bs == null) { - // return data; - // } - // - // if (node.isSingleCharacterStringLiteral()) { - // if (!InefficientStringBufferingRule.isInStringBufferOperationChain(node, "append")) { - // return data; - // } - // - // // ignore, if the literal is part of an expression, such as "X".repeat(5) - // final ASTPrimaryExpression primaryExpression = (ASTPrimaryExpression) node.getNthParent(2); - // if (primaryExpression != null && primaryExpression.getFirstChildOfType(ASTPrimarySuffix.class) != null) { - // return data; - // } - // // ignore, if this literal is part of a different expression, e.g. "X" + something else - // if (primaryExpression != null && !(primaryExpression.getNthParent(2) instanceof ASTArgumentList)) { - // return data; - // } - // // ignore if this string literal is used as a constructor argument - // if (primaryExpression != null && primaryExpression.getNthParent(4) instanceof ASTAllocationExpression) { - // return data; - // } - // - // addViolation(data, node); - // } + public Object visit(ASTStringLiteral node, Object data) { + if (node.length() == 1 && node.getParent() instanceof ASTArgumentList + && ((ASTArgumentList) node.getParent()).size() == 1) { + JavaNode callParent = node.getParent().getParent(); + if (callParent instanceof ASTMethodCall) { + ASTMethodCall call = (ASTMethodCall) callParent; + if (call.getMethodName().equals("append") + && (TypeTestUtil.isDeclaredInClass(StringBuilder.class, call.getMethodType()) + || TypeTestUtil.isDeclaredInClass(StringBuffer.class, call.getMethodType())) + ) { + addViolation(data, node); + } + } + } return data; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java index 7ae956357b..117fde1b94 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java @@ -52,7 +52,7 @@ public class UselessStringValueOfRule extends AbstractJavaRule { ASTMethodCall call = (ASTMethodCall) expr; if (call.getArguments().size() == 1 && call.getMethodName().equals("valueOf") - && TypeTestUtil.isExactlyA(String.class, call.getMethodType().getDeclaringType().getSymbol())) { + && TypeTestUtil.isDeclaredInClass(String.class, call.getMethodType())) { return call.getArguments().get(0); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index 341a39fd9c..efa3cf4967 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -191,6 +191,19 @@ public final class TypeTestUtil { return symClass.getBinaryName().equals(klass.getName()); } + /** + * Returns true if the signature is that of a method declared in the + * given class. + * + * @param klass Class + * @param sig Method signature to test + * + * @throws NullPointerException If any argument is null + */ + public static boolean isDeclaredInClass(@NonNull Class klass, @NonNull JMethodSig sig) { + return isExactlyA(klass, sig.getDeclaringType().getSymbol()); + } + /** * Checks whether the static type of the node is exactly the type diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharTest.java index d3d01c6875..d289d83430 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AppendCharacterWithCharTest extends PmdRuleTst { // no additional unit tests } From 37ce1447bb88d27958f5f9892709318106a5cc03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 23:26:13 +0100 Subject: [PATCH 062/299] Update StringInstantiation --- .travis/all-java.xml | 2 +- .../performance/StringInstantiationRule.java | 83 ++++--------------- .../performance/StringInstantiationTest.java | 1 - 3 files changed, 15 insertions(+), 71 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index ebf95cdf4a..99ed47b311 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -306,7 +306,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationRule.java index 57a3b8569c..36ac315fec 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationRule.java @@ -4,87 +4,32 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import static net.sourceforge.pmd.lang.ast.NodeStream.asInstanceOf; +import org.checkerframework.checker.nullness.qual.NonNull; -import java.util.List; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAdditiveExpression; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTArguments; -import net.sourceforge.pmd.lang.java.ast.ASTArrayDimsAndInits; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.TypedNameDeclaration; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.symboltable.NameDeclaration; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; public class StringInstantiationRule extends AbstractJavaRule { - public StringInstantiationRule() { - addRuleChainVisit(ASTAllocationExpression.class); + @Override + protected @NonNull RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(ASTConstructorCall.class); } @Override - public Object visit(ASTAllocationExpression node, Object data) { - if (!(node.getChild(0) instanceof ASTClassOrInterfaceType)) { - return data; - } - - if (!TypeTestUtil.isA(String.class, (ASTClassOrInterfaceType) node.getChild(0))) { - return data; - } - - if (isArrayAccess(node)) { - addViolation(data, node); - return data; - } - - List exp = node.findDescendantsOfType(ASTExpression.class); - if (exp.size() >= 2) { - return data; - } - - if (node.descendants().map(asInstanceOf(ASTArrayDimsAndInits.class, ASTAdditiveExpression.class)).nonEmpty()) { - return data; - } - - ASTName name = node.getFirstDescendantOfType(ASTName.class); - // Literal, i.e., new String("foo") - if (name == null) { - addViolation(data, node); - return data; - } - - NameDeclaration nd = name.getNameDeclaration(); - if (!(nd instanceof TypedNameDeclaration)) { - return data; - } - - if (TypeTestUtil.isA(String.class, ((TypedNameDeclaration) nd).getTypeNode())) { + public Object visit(ASTConstructorCall node, Object data) { + ASTArgumentList args = node.getArguments(); + if (args.size() <= 1 + && TypeTestUtil.isExactlyA(String.class, node.getTypeNode())) { + if (args.size() == 1 && TypeTestUtil.isExactlyA(byte[].class, args.get(0))) { + // byte array ctor is ok + return data; + } addViolation(data, node); } return data; } - - private boolean isArrayAccess(ASTAllocationExpression node) { - ASTArguments arguments = node.getFirstChildOfType(ASTArguments.class); - if (arguments == null || arguments.size() != 1) { - return false; - } - - Node firstArg = arguments.getFirstChildOfType(ASTArgumentList.class).getChild(0); - ASTPrimaryExpression primary = firstArg.getFirstChildOfType(ASTPrimaryExpression.class); - if (primary == null || primary.getType() != String.class) { - return false; - } - - ASTPrimarySuffix suffix = primary.getFirstChildOfType(ASTPrimarySuffix.class); - return suffix != null && suffix.isArrayDereference(); - } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationTest.java index f5300916e1..fc6fdbdd97 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/StringInstantiationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class StringInstantiationTest extends PmdRuleTst { // no additional unit tests } From 73deec4e7cf0f06e7b2b4c8406670e84d0bbf622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 23:30:21 +0100 Subject: [PATCH 063/299] Update SimplifyStartsWith --- .travis/all-java.xml | 2 +- .../src/main/resources/category/java/performance.xml | 12 ++---------- .../rule/performance/SimplifyStartsWithTest.java | 1 - .../java/rule/performance/xml/SimplifyStartsWith.xml | 7 +++++-- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 99ed47b311..a100858461 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -305,7 +305,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index b69d020fb6..6d36be1577 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -683,16 +683,8 @@ ensure that the string is not empty by making an additional check first. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/SimplifyStartsWithTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/SimplifyStartsWithTest.java index dea1a15667..e2a3fbcd15 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/SimplifyStartsWithTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/SimplifyStartsWithTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SimplifyStartsWithTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml index 81e7958516..fa36e3c7a0 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml @@ -40,7 +40,6 @@ public class Foo { ]]> - #1392 SimplifyStartsWith false-negative @@ -66,6 +64,11 @@ public class SimplifyStartsWith { || nextSibling.getType() == JavadocTokenTypes.EOF || nextSibling.getText().startsWith(" "); } + + static class DetailNode { + String getText() { return ""; } + } + } ]]> From 4a35778d55438d2b8f81959f47a23ed0be282af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 23:32:40 +0100 Subject: [PATCH 064/299] Update TooFewBranchesForASwitchStatement --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/performance.xml | 4 +--- .../performance/TooFewBranchesForASwitchStatementTest.java | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index a100858461..a05ae8718e 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -308,7 +308,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 6d36be1577..c37cc870ad 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -789,9 +789,7 @@ if-then statement to increase code readability. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/TooFewBranchesForASwitchStatementTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/TooFewBranchesForASwitchStatementTest.java index efc2a971e2..730ab7469e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/TooFewBranchesForASwitchStatementTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/TooFewBranchesForASwitchStatementTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class TooFewBranchesForASwitchStatementTest extends PmdRuleTst { // no additional unit tests } From b74b8f19cf6dfd37bf06c2eecb59e23ab60cd420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 23:36:12 +0100 Subject: [PATCH 065/299] Update AvoidSynchronizedAtMethodLevel --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/multithreading.xml | 2 +- .../rule/multithreading/AvoidSynchronizedAtMethodLevelTest.java | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index a05ae8718e..cedbeda11e 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -272,7 +272,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index e1a7a30368..a78517cf59 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -23,7 +23,7 @@ gets it. 3 - //MethodDeclaration[@Synchronized = true()] + //MethodDeclaration[pmd-java:modifiers() = "synchronized"] diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidSynchronizedAtMethodLevelTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidSynchronizedAtMethodLevelTest.java index 1661961b6e..5c1793c6d8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidSynchronizedAtMethodLevelTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidSynchronizedAtMethodLevelTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidSynchronizedAtMethodLevelTest extends PmdRuleTst { // no additional unit tests } From db506d7e1ae5375ac085009360f5443437119d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 23:37:46 +0100 Subject: [PATCH 066/299] Update AvoidThreadGroup --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/multithreading.xml | 4 ++-- .../lang/java/rule/multithreading/AvoidThreadGroupTest.java | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index cedbeda11e..869651dec8 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -273,7 +273,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index a78517cf59..cbad9f2150 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -87,8 +87,8 @@ it contains methods that are not thread-safe. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidThreadGroupTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidThreadGroupTest.java index 6cfdfccf71..2afcbaa379 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidThreadGroupTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidThreadGroupTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidThreadGroupTest extends PmdRuleTst { // Used by AvoidThreadGroup test cases public static class ThreadGroup { From 28e2d0b41b4b92ec3960556f7eda417b84ac42ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 23:38:38 +0100 Subject: [PATCH 067/299] Update AvoidUsingVolatile --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/multithreading.xml | 2 +- .../lang/java/rule/multithreading/AvoidUsingVolatileTest.java | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 869651dec8..014a9e2cf1 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -274,7 +274,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index cbad9f2150..191e194b85 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -121,7 +121,7 @@ the volatile keyword should not be used for maintenance purpose and portability. 2 - //FieldDeclaration[@Volatile = true()] + //FieldDeclaration[pmd-java:modifiers() = "volatile"] diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidUsingVolatileTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidUsingVolatileTest.java index 303f0fab0b..ce61a3e5c6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidUsingVolatileTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/AvoidUsingVolatileTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidUsingVolatileTest extends PmdRuleTst { // no additional unit tests } From 7ed9db65506279148dd6ab25ab8bb23a95ff1ba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 29 Oct 2020 23:44:13 +0100 Subject: [PATCH 068/299] Update DoNotUseThreads --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/multithreading.xml | 4 ++-- .../lang/java/rule/multithreading/DoNotUseThreadsTest.java | 1 - .../pmd/lang/java/rule/multithreading/xml/DoNotUseThreads.xml | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 014a9e2cf1..ce0139e230 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -275,7 +275,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index 191e194b85..539d595549 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -150,9 +150,9 @@ Also EJB's might be moved between machines in a cluster and only managed resourc diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoNotUseThreadsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoNotUseThreadsTest.java index 586f1dffc2..9ba3e87d61 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoNotUseThreadsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoNotUseThreadsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DoNotUseThreadsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DoNotUseThreads.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DoNotUseThreads.xml index 60dd425d0a..fa658bee70 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DoNotUseThreads.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DoNotUseThreads.xml @@ -65,7 +65,7 @@ public class ExecutorServiceUsage { Using Executors directly is not allowed - 2 + 4 Date: Thu, 29 Oct 2020 23:46:01 +0100 Subject: [PATCH 069/299] Update DontCallThreadRun --- .travis/all-java.xml | 2 +- .../main/resources/category/java/multithreading.xml | 12 +----------- .../rule/multithreading/DontCallThreadRunTest.java | 1 - 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index ce0139e230..1b64bbfa23 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -276,7 +276,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index 539d595549..4326229483 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -207,17 +207,7 @@ Explicitly calling Thread.run() method will execute in the caller's thread of co diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DontCallThreadRunTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DontCallThreadRunTest.java index a3977158b5..c628ed4b78 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DontCallThreadRunTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DontCallThreadRunTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DontCallThreadRunTest extends PmdRuleTst { // Used by DontCallThreadRun test cases public static class TestThread extends Thread { From a9fcb8f20b665cb53989bc9d870d60eed9ebd978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 30 Oct 2020 00:14:01 +0100 Subject: [PATCH 070/299] Update DoubleCheckedLocking --- .travis/all-java.xml | 2 +- .../DoubleCheckedLockingRule.java | 262 +++++------------- .../DoubleCheckedLockingTest.java | 1 - 3 files changed, 77 insertions(+), 188 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 1b64bbfa23..30dee7d104 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -277,7 +277,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java index 5d42d5c6b0..999ac2572b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java @@ -4,32 +4,28 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; -import java.util.ArrayList; +import java.lang.reflect.Modifier; import java.util.List; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAssignmentOperator; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; -import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; +import net.sourceforge.pmd.lang.java.ast.ASTAssignmentExpression; import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -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.ASTReferenceType; +import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement; -import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; import net.sourceforge.pmd.lang.java.ast.ASTSynchronizedStatement; -import net.sourceforge.pmd.lang.java.ast.ASTType; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.ast.ASTVariableInitializer; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; +import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol; +import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; /** *

@@ -58,97 +54,55 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule;
  */
 public class DoubleCheckedLockingRule extends AbstractJavaRule {
 
-    private List volatileFields;
-
     @Override
-    public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
-        if (node.isInterface()) {
-            return data;
-        }
-        return super.visit(node, data);
-    }
-
-    @Override
-    public Object visit(ASTCompilationUnit compilationUnit, Object data) {
-        if (this.volatileFields == null) {
-            this.volatileFields = new ArrayList<>(0);
-        } else {
-            this.volatileFields.clear();
-        }
-        return super.visit(compilationUnit, data);
-    }
-
-    @Override
-    public Object visit(ASTFieldDeclaration fieldDeclaration, Object data) {
-        if (fieldDeclaration.isVolatile()) {
-            for (ASTVariableDeclaratorId declarator : fieldDeclaration
-                    .findDescendantsOfType(ASTVariableDeclaratorId.class)) {
-                this.volatileFields.add(declarator.getImage());
-            }
-        }
-        return super.visit(fieldDeclaration, data);
+    protected @NonNull RuleTargetSelector buildTargetSelector() {
+        return RuleTargetSelector.forTypes(ASTMethodDeclaration.class);
     }
 
     @Override
     public Object visit(ASTMethodDeclaration node, Object data) {
-        if (node.getResultType().isVoid()) {
-            return super.visit(node, data);
+        if (node.isVoid() || node.getResultTypeNode() instanceof ASTPrimitiveType || node.getBody() == null) {
+            return data;
         }
 
-        ASTType typeNode = (ASTType) node.getResultType().getChild(0);
-        if (typeNode.getNumChildren() == 0 || !(typeNode.getChild(0) instanceof ASTReferenceType)) {
-            return super.visit(node, data);
-        }
-
-        List rsl = node.findDescendantsOfType(ASTReturnStatement.class);
+        List rsl = node.descendants(ASTReturnStatement.class).toList();
         if (rsl.size() != 1) {
-            return super.visit(node, data);
+            return data;
         }
         ASTReturnStatement rs = rsl.get(0);
 
-        List pel = rs.findDescendantsOfType(ASTPrimaryExpression.class);
-        ASTPrimaryExpression ape = pel.get(0);
-        Node lastChild = ape.getChild(ape.getNumChildren() - 1);
-        String returnVariableName = null;
-        if (lastChild instanceof ASTPrimaryPrefix) {
-            returnVariableName = getNameFromPrimaryPrefix((ASTPrimaryPrefix) lastChild);
+        ASTExpression returnExpr = rs.getExpr();
+        if (!(returnExpr instanceof ASTNamedReferenceExpr)) {
+            return data;
         }
+
+        JVariableSymbol returnVariable = ((ASTNamedReferenceExpr) returnExpr).getReferencedSym();
         // With Java5 and volatile keyword, DCL is no longer an issue
-        if (returnVariableName == null || this.volatileFields.contains(returnVariableName)) {
-            return super.visit(node, data);
+        if (returnVariable instanceof JFieldSymbol
+            && Modifier.isVolatile(((JFieldSymbol) returnVariable).getModifiers())) {
+            return data;
         }
+
         // if the return variable is local and only written with the volatile
         // field, then it's ok, too
-        if (checkLocalVariableUsage(node, returnVariableName)) {
+        if (isLocalOnlyStoredWithVolatileField(node, returnVariable)) {
             return super.visit(node, data);
         }
+
         List isl = node.findDescendantsOfType(ASTIfStatement.class);
         if (isl.size() == 2) {
-            ASTIfStatement is = isl.get(0);
-            if (ifVerify(is, returnVariableName)) {
+            ASTIfStatement outerIf = isl.get(0);
+            if (isNullCheck(outerIf.getCondition(), returnVariable)) {
                 // find synchronized
-                List ssl = is.findDescendantsOfType(ASTSynchronizedStatement.class);
-                if (ssl.size() == 1) {
-                    ASTSynchronizedStatement ss = ssl.get(0);
-                    isl = ss.findDescendantsOfType(ASTIfStatement.class);
-                    if (isl.size() == 1) {
-                        ASTIfStatement is2 = isl.get(0);
-                        if (ifVerify(is2, returnVariableName)) {
-                            List sel = is2.findDescendantsOfType(ASTStatementExpression.class);
-                            if (sel.size() == 1) {
-                                ASTStatementExpression se = sel.get(0);
-                                if (se.getNumChildren() == 3) {
-                                    // primaryExpression, AssignmentOperator, Expression
-                                    if (se.getChild(0) instanceof ASTPrimaryExpression) {
-                                        ASTPrimaryExpression pe = (ASTPrimaryExpression) se.getChild(0);
-                                        if (matchName(pe, returnVariableName)) {
-                                            if (se.getChild(1) instanceof ASTAssignmentOperator) {
-                                                addViolation(data, node);
-                                            }
-                                        }
-                                    }
-                                }
-                            }
+                List ssl = outerIf.findDescendantsOfType(ASTSynchronizedStatement.class);
+                if (ssl.size() == 1 && ssl.get(0).ancestors().any(it -> it == outerIf)) {
+                    ASTIfStatement is2 = isl.get(1);
+                    if (isNullCheck(is2.getCondition(), returnVariable)) {
+                        List assignments = is2.findDescendantsOfType(ASTAssignmentExpression.class);
+                        if (assignments.size() == 1
+                            && isReferenceTo(assignments.get(0).getLeftOperand(), returnVariable)) {
+                            addViolation(data, node);
+
                         }
                     }
                 }
@@ -157,118 +111,54 @@ public class DoubleCheckedLockingRule extends AbstractJavaRule {
         return super.visit(node, data);
     }
 
-    private boolean checkLocalVariableUsage(ASTMethodDeclaration node, String returnVariableName) {
-        List locals = node.findDescendantsOfType(ASTLocalVariableDeclaration.class);
-        ASTVariableInitializer initializer = null;
-        for (ASTLocalVariableDeclaration l : locals) {
-            ASTVariableDeclaratorId id = l.getFirstDescendantOfType(ASTVariableDeclaratorId.class);
-            if (id != null && id.hasImageEqualTo(returnVariableName)) {
-                initializer = l.getFirstDescendantOfType(ASTVariableInitializer.class);
-                break;
-            }
-        }
-        // the return variable name doesn't seem to be a local variable
-        if (initializer == null) {
-            return false;
-        }
-
-        // verify the value with which the local variable is initialized
-        if (initializer.getNumChildren() > 0 && initializer.getChild(0) instanceof ASTExpression
-                && initializer.getChild(0).getNumChildren() > 0
-                && initializer.getChild(0).getChild(0) instanceof ASTPrimaryExpression
-                && initializer.getChild(0).getChild(0).getNumChildren() > 0
-                && initializer.getChild(0).getChild(0).getChild(0) instanceof ASTPrimaryPrefix
-                && initializer.getChild(0).getChild(0).getChild(0).getNumChildren() > 0
-                && initializer.getChild(0).getChild(0).getChild(0).getChild(0) instanceof ASTName) {
-            ASTName name = (ASTName) initializer.getChild(0).getChild(0).getChild(0).getChild(0);
-            if (name == null || !volatileFields.contains(name.getImage())) {
+    private boolean isLocalOnlyStoredWithVolatileField(ASTMethodDeclaration method, JVariableSymbol local) {
+        ASTExpression initializer;
+        if (local instanceof JLocalVariableSymbol) {
+            ASTVariableDeclaratorId id = local.tryGetNode();
+            if (id == null) {
                 return false;
             }
+            initializer = id.getInitializer();
         } else {
-            // not a simple assignment
+            // the return variable name doesn't seem to be a local variable
             return false;
         }
 
-        // now check every usage/assignment of the variable
-        List names = node.findDescendantsOfType(ASTName.class);
-        for (ASTName n : names) {
-            if (!n.hasImageEqualTo(returnVariableName)) {
-                continue;
-            }
-
-            Node expression = n.getNthParent(3);
-            if (expression instanceof ASTEqualityExpression) {
-                continue;
-            }
-            if (expression instanceof ASTStatementExpression) {
-                if (expression.getNumChildren() > 2 && expression.getChild(1) instanceof ASTAssignmentOperator) {
-                    ASTName value = expression.getChild(2).getFirstDescendantOfType(ASTName.class);
-                    if (value == null || !volatileFields.contains(value.getImage())) {
-                        return false;
-                    }
-                }
-            }
-        }
-
-        return true;
+        return (initializer == null || isVolatileFieldReference(initializer))
+            && method.descendants(ASTAssignmentExpression.class)
+                     .filter(it -> isReferenceTo(it.getLeftOperand(), local))
+                     .all(it -> isVolatileFieldReference(it.getRightOperand()));
     }
 
-    private boolean ifVerify(ASTIfStatement is, String varname) {
-        List finder = is.findDescendantsOfType(ASTPrimaryExpression.class);
-        if (finder.size() > 1) {
-            ASTPrimaryExpression nullStmt = findNonVariableStmt(varname, finder.get(0), finder.get(1));
-            if (nullStmt != null) {
-                if (nullStmt.getNumChildren() == 1 && nullStmt.getChild(0) instanceof ASTPrimaryPrefix) {
-                    ASTPrimaryPrefix pp2 = (ASTPrimaryPrefix) nullStmt.getChild(0);
-                    if (pp2.getNumChildren() == 1 && pp2.getChild(0) instanceof ASTLiteral) {
-                        ASTLiteral lit = (ASTLiteral) pp2.getChild(0);
-                        if (lit.getNumChildren() == 1 && lit.getChild(0) instanceof ASTNullLiteral) {
-                            return true;
-                        }
-                    }
+    private boolean isVolatileFieldReference(@Nullable ASTExpression initializer) {
+        if (initializer instanceof ASTNamedReferenceExpr) {
+            JVariableSymbol fieldSym = ((ASTNamedReferenceExpr) initializer).getReferencedSym();
+            return fieldSym instanceof JFieldSymbol && Modifier.isVolatile(((JFieldSymbol) fieldSym).getModifiers());
+        } else {
+            return false;
+        }
+    }
+
+    private boolean isReferenceTo(@Nullable ASTExpression expr, JVariableSymbol symbol) {
+        if (expr instanceof ASTNamedReferenceExpr) {
+            return symbol != null && symbol.equals(((ASTNamedReferenceExpr) expr).getReferencedSym());
+        } else {
+            return false;
+        }
+    }
+
+    private boolean isNullCheck(ASTExpression expr, JVariableSymbol var) {
+        if (expr instanceof ASTInfixExpression) {
+            ASTInfixExpression condition = (ASTInfixExpression) expr;
+            if (condition.getOperator().isEquality()) {
+                ASTNullLiteral nullLit = condition.getFirstChildOfType(ASTNullLiteral.class);
+                if (nullLit != null) {
+                    ASTExpression otherChild = (ASTExpression) condition.getChild(1 - nullLit.getIndexInParent());
+                    return isReferenceTo(otherChild, var);
                 }
             }
         }
         return false;
     }
 
-    /**
-     * 

- * Sort out if apeLeft or apeRight are variable with the provided - * 'variableName'. - *

- * - * @param variableName - * @param apeLeft - * @param apeRight - * @return reference from either apeLeft or apeRight, if one of them match, - * or 'null', if none match. - */ - private ASTPrimaryExpression findNonVariableStmt(String variableName, ASTPrimaryExpression apeLeft, - ASTPrimaryExpression apeRight) { - if (matchName(apeLeft, variableName)) { - return apeRight; - } else if (matchName(apeRight, variableName)) { - return apeLeft; - } - return null; - } - - private boolean matchName(ASTPrimaryExpression ape, String name) { - if (ape.getNumChildren() == 1 && ape.getChild(0) instanceof ASTPrimaryPrefix) { - ASTPrimaryPrefix pp = (ASTPrimaryPrefix) ape.getChild(0); - String name2 = getNameFromPrimaryPrefix(pp); - if (name2 != null && name2.equals(name)) { - return true; - } - } - return false; - } - - private String getNameFromPrimaryPrefix(ASTPrimaryPrefix pp) { - if (pp.getNumChildren() == 1 && pp.getChild(0) instanceof ASTName) { - return ((ASTName) pp.getChild(0)).getImage(); - } - return null; - } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingTest.java index 658afce33c..201f12e621 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class DoubleCheckedLockingTest extends PmdRuleTst { // no additional unit tests } From c72c108b5fcdc688d56cfcc6809c112a3f4fcc72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 30 Oct 2020 00:20:49 +0100 Subject: [PATCH 071/299] Update UseConcurrentHashMap --- .../main/resources/category/java/multithreading.xml | 3 +-- .../multithreading/UseConcurrentHashMapTest.java | 1 - .../multithreading/xml/UseConcurrentHashMap.xml | 13 ++++++++----- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index 4326229483..7c2a437120 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -338,8 +338,7 @@ perform efficient map reads without blocking other threads. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseConcurrentHashMapTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseConcurrentHashMapTest.java index bca9699fb5..2ddf93e4e9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseConcurrentHashMapTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseConcurrentHashMapTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseConcurrentHashMapTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml index cce05f421b..c603a04286 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml @@ -8,6 +8,7 @@ Basic use case 2 #1034 UseConcurrentHashMap flags calls to methods that return Map 0 @@ -37,15 +41,14 @@ public class Foo { #1342 UseConcurrentHashMap false positive (with documentation example) 1 - 3 + 5 From 0f2cde21a3d0c7971e5a3aa7c11ba068450c1412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 30 Oct 2020 00:25:27 +0100 Subject: [PATCH 072/299] Update UseNotifyAllInsteadOfNotify --- .travis/all-java.xml | 4 ++-- .../main/resources/category/java/multithreading.xml | 10 +--------- .../UseNotifyAllInsteadOfNotifyTest.java | 1 - .../multithreading/xml/UseNotifyAllInsteadOfNotify.xml | 3 +++ 4 files changed, 6 insertions(+), 12 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 30dee7d104..9fc9f18f03 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -280,8 +280,8 @@ - - + + diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index 7c2a437120..d97c58cfea 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -373,15 +373,7 @@ one is chosen. The thread chosen is arbitrary; thus its usually safer to call n diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseNotifyAllInsteadOfNotifyTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseNotifyAllInsteadOfNotifyTest.java index 1b16dd00f6..c03cbd61d0 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseNotifyAllInsteadOfNotifyTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/multithreading/UseNotifyAllInsteadOfNotifyTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.multithreading; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseNotifyAllInsteadOfNotifyTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseNotifyAllInsteadOfNotify.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseNotifyAllInsteadOfNotify.xml index 4630020e38..e17d7ed51a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseNotifyAllInsteadOfNotify.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseNotifyAllInsteadOfNotify.xml @@ -9,6 +9,7 @@ 1 2 Date: Fri, 30 Oct 2020 08:44:21 +0100 Subject: [PATCH 073/299] Update InefficientEmptyStringCheck --- .travis/all-java.xml | 2 +- .../InefficientEmptyStringCheckRule.java | 82 ++++++++++++------- .../InefficientEmptyStringCheckTest.java | 1 - .../pmd/lang/java/types/TypeTestUtilTest.java | 2 +- .../xml/InefficientEmptyStringCheck.xml | 3 + 5 files changed, 57 insertions(+), 33 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 9fc9f18f03..fdac7988af 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -297,7 +297,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java index 63ad694116..db33f3af3d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java @@ -4,10 +4,13 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.rule.AbstractInefficientZeroCheck; -import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** * This rule finds code which inefficiently determines empty strings. @@ -41,41 +44,60 @@ import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; * * @author acaplan */ -public class InefficientEmptyStringCheckRule extends AbstractInefficientZeroCheck { +public class InefficientEmptyStringCheckRule extends AbstractJavaRulechainRule { + + public InefficientEmptyStringCheckRule() { + super(ASTMethodCall.class); + } @Override - public boolean isTargetMethod(JavaNameOccurrence occ) { - if (occ.getNameForWhichThisIsAQualifier() != null - && occ.getNameForWhichThisIsAQualifier().getImage().contains("trim")) { - Node pExpression = occ.getLocation().getParent().getParent(); - if (pExpression.getNumChildren() > 2 && "length".equals(pExpression.getChild(2).getImage())) { - return true; - } + public Object visit(ASTMethodCall call, Object data) { + if (isTrimCall(call.getQualifier()) + && (isLengthZeroCheck(call) || isIsEmptyCall(call))) { + addViolation(data, call); + } + return null; + } + + private static boolean isLengthZeroCheck(ASTMethodCall call) { + return call.getMethodName().equals("length") + && call.getArguments().size() == 0 + && isZeroCheck(call.getParent(), 1 - call.getIndexInParent()); + } + + private static boolean isZeroCheck(JavaNode e, int checkLiteralAtIdx) { + if (e instanceof ASTInfixExpression) { + return ((ASTInfixExpression) e).getOperator().isEquality() + && isIntLit(e.getChild(checkLiteralAtIdx), 0); } return false; } - @Override - public boolean appliesToClassName(String name) { - return "String".equals(name); + private static boolean isIntLit(JavaNode e, int value) { + if (e instanceof ASTNumericLiteral) { + return ((ASTNumericLiteral) e).getValueAsInt() == value; + } + return false; } - @Override - public Object visit(ASTPrimaryExpression node, Object data) { - - if (node.getNumChildren() > 3) { - // Check last suffix - if (!"isEmpty".equals(node.getChild(node.getNumChildren() - 2).getImage())) { - return data; - } - - Node prevCall = node.getChild(node.getNumChildren() - 4); - String target = prevCall.getNumChildren() > 0 ? prevCall.getChild(0).getImage() : prevCall.getImage(); - if (target != null && ("trim".equals(target) || target.endsWith(".trim"))) { - addViolation(data, node); - } + private static boolean isTrimCall(ASTExpression expr) { + if (expr instanceof ASTMethodCall) { + ASTMethodCall call = (ASTMethodCall) expr; + return call.getMethodName().equals("trim") + && call.getArguments().size() == 0 + && TypeTestUtil.isA(String.class, call.getQualifier()); } - return data; + return false; + } + + + private static boolean isIsEmptyCall(ASTExpression expr) { + if (expr instanceof ASTMethodCall) { + ASTMethodCall call = (ASTMethodCall) expr; + return call.getMethodName().equals("isEmpty") + && call.getArguments().size() == 0; + } + return false; } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckTest.java index 6d3628ad5b..3adeb48d5b 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class InefficientEmptyStringCheckTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java index 402d4ef25a..7ab99de7b5 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java @@ -188,7 +188,7 @@ public class TypeTestUtilTest extends BaseNonParserTest { public void testNullNode() { Assert.assertFalse(TypeTestUtil.isA(String.class, null)); Assert.assertFalse(TypeTestUtil.isA("java.lang.String", null)); - Assert.assertFalse(TypeTestUtil.isExactlyA(String.class, null)); + Assert.assertFalse(TypeTestUtil.isExactlyA(String.class, (TypeNode) null)); Assert.assertFalse(TypeTestUtil.isExactlyA("java.lang.String", null)); } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientEmptyStringCheck.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientEmptyStringCheck.xml index e77c303c83..77df28a871 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientEmptyStringCheck.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientEmptyStringCheck.xml @@ -144,6 +144,8 @@ public class Foo { String.trim().isEmpty() is called after a chain call, should have failed 2 String.trim().isEmpty() is called after a chain call, should have failed twice 2 Date: Fri, 30 Oct 2020 09:30:54 +0100 Subject: [PATCH 074/299] Update UseCollectionsIsEmpty --- .travis/all-java.xml | 2 +- .../rule/AbstractInefficientZeroCheck.java | 159 ------------------ .../UseCollectionIsEmptyRule.java | 132 ++------------- .../lang/java/rule/internal/JavaRuleUtil.java | 69 ++++++++ .../InefficientEmptyStringCheckRule.java | 21 +-- .../UseCollectionIsEmptyTest.java | 1 - .../xml/UseCollectionIsEmpty.xml | 23 ++- 7 files changed, 104 insertions(+), 303 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractInefficientZeroCheck.java create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java diff --git a/.travis/all-java.xml b/.travis/all-java.xml index fdac7988af..3159631fae 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -58,7 +58,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractInefficientZeroCheck.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractInefficientZeroCheck.java deleted file mode 100644 index 499e5efc9f..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractInefficientZeroCheck.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; -import net.sourceforge.pmd.lang.java.ast.ASTRelationalExpression; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; - -/** - * This is an abstract rule for patterns which compare a method invocation to 0. - * It could be further abstracted to find code that compares something to - * another definable pattern - * - * @author acaplan - * @deprecated Internal API - */ -@Deprecated -@InternalApi -public abstract class AbstractInefficientZeroCheck extends AbstractJavaRule { - - private static Map inverse = new HashMap<>(); - - public abstract boolean appliesToClassName(String name); - - public abstract boolean isTargetMethod(JavaNameOccurrence occ); - - /** - * For each relation/equality operator, comparison targets need to define. - * - * @return map - */ - public Map> getComparisonTargets() { - Map> rules = new HashMap<>(); - rules.put("==", Arrays.asList("0")); - rules.put("!=", Arrays.asList("0")); - rules.put(">", Arrays.asList("0")); - rules.put("<", Arrays.asList("0")); - return rules; - } - - static { - inverse.put("<", ">"); - inverse.put(">", "<"); - inverse.put("<=", ">="); - inverse.put(">=", "<="); - inverse.put("==", "=="); - inverse.put("!=", "!="); - } - - @Override - public Object visit(ASTVariableDeclaratorId node, Object data) { - Node nameNode = node.getTypeNameNode(); - if (nameNode == null || nameNode instanceof ASTPrimitiveType - || node.getNameDeclaration() == null - || !appliesToClassName(node.getNameDeclaration().getTypeImage())) { - return data; - } - - List declars = node.getUsages(); - for (NameOccurrence occ : declars) { - JavaNameOccurrence jocc = (JavaNameOccurrence) occ; - if (!isTargetMethod(jocc)) { - continue; - } - Node expr = jocc.getLocation().getParent().getParent().getParent(); - checkNodeAndReport(data, jocc.getLocation(), expr); - } - return data; - } - - /** - * Checks whether the given expression is a equality/relation expression - * that compares with a size() call. - * - * @param data - * the rule context - * @param location - * the node location to report - * @param expr - * the ==, <, > expression - */ - protected void checkNodeAndReport(Object data, Node location, Node expr) { - if ((expr instanceof ASTEqualityExpression - || expr instanceof ASTRelationalExpression && getComparisonTargets().containsKey(expr.getImage())) - && isCompare(expr)) { - addViolation(data, location); - } - } - - /** - * We only need to report if this is comparing against one of the comparison - * targets - * - * @param equality - * @return true if this is comparing to one of the comparison targets else - * false - * @see #getComparisonTargets() - */ - private boolean isCompare(Node equality) { - if (isLiteralLeftHand(equality)) { - return checkComparison(inverse.get(equality.getImage()), equality, 0); - } else if (isLiteralRightHand(equality)) { - return checkComparison(equality.getImage(), equality, 1); - } - return false; - } - - private boolean isLiteralLeftHand(Node equality) { - return isLiteral(equality, 0); - } - - private boolean isLiteralRightHand(Node equality) { - return isLiteral(equality, 1); - } - - private boolean isLiteral(Node equality, int child) { - Node target = equality.getChild(child); - target = getFirstChildOrThis(target); - target = getFirstChildOrThis(target); - return target instanceof ASTLiteral; - } - - private Node getFirstChildOrThis(Node node) { - if (node.getNumChildren() > 0) { - return node.getChild(0); - } - return node; - } - - /** - * Checks if the equality expression passed in is of comparing against the - * value passed in as i - * - * @param equality - * @param i - * The ordinal in the equality expression to check - * @return true if the value in position i is one of the comparison targets, - * else false - * @see #getComparisonTargets() - */ - private boolean checkComparison(String operator, Node equality, int i) { - Node target = equality.getChild(i).getChild(0).getChild(0); - return target instanceof ASTLiteral && getComparisonTargets().get(operator).contains(target.getImage()); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java index e1b8bf7d89..f313f76be2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java @@ -4,31 +4,13 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; - import java.util.Collection; -import java.util.HashMap; -import java.util.List; import java.util.Map; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; -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.ASTResultType; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; -import net.sourceforge.pmd.lang.java.rule.AbstractInefficientZeroCheck; -import net.sourceforge.pmd.lang.java.symboltable.ClassScope; -import net.sourceforge.pmd.lang.java.symboltable.JavaNameOccurrence; -import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; -import net.sourceforge.pmd.util.CollectionUtil; /** * Detect structures like "foo.size() == 0" and suggest replacing them with @@ -36,109 +18,25 @@ import net.sourceforge.pmd.util.CollectionUtil; * * @author Jason Bennett */ -public class UseCollectionIsEmptyRule extends AbstractInefficientZeroCheck { +public class UseCollectionIsEmptyRule extends AbstractJavaRulechainRule { - @Override - public boolean appliesToClassName(String name) { - return CollectionUtil.isCollectionType(name, true); - } - - /** - * Determine if we're dealing with .size method - * - * @param occ - * The name occurrence - * @return true if it's .size, else false - */ - @Override - public boolean isTargetMethod(JavaNameOccurrence occ) { - return occ.getNameForWhichThisIsAQualifier() != null - && occ.getLocation().getImage().endsWith(".size"); + public UseCollectionIsEmptyRule() { + super(ASTMethodCall.class); } @Override - public Map> getComparisonTargets() { - List zeroAndOne = asList("0", "1"); - List zero = singletonList("0"); - Map> rules = new HashMap<>(); - rules.put("<", zeroAndOne); - rules.put(">", zero); - rules.put("==", zero); - rules.put("!=", zero); - rules.put(">=", zeroAndOne); - rules.put("<=", zero); - return rules; - } - - @Override - public Object visit(ASTPrimarySuffix node, Object data) { - if (isSizeMethodCall(node) && isCalledOnCollection(node)) { - Node expr = node.getParent().getParent(); - checkNodeAndReport(data, node, expr); - } - return data; - } - - private boolean isSizeMethodCall(ASTPrimarySuffix primarySuffix) { - String calledMethodName = primarySuffix.getImage(); - return calledMethodName != null && calledMethodName.endsWith("size"); - } - - private boolean isCalledOnCollection(ASTPrimarySuffix primarySuffix) { - ASTClassOrInterfaceType calledOnType = getTypeOfVariable(primarySuffix); - if (calledOnType == null) { - calledOnType = getTypeOfMethodCall(primarySuffix); - } - return TypeTestUtil.isA(Collection.class, calledOnType); - } - - private ASTClassOrInterfaceType getTypeOfVariable(ASTPrimarySuffix primarySuffix) { - ASTPrimaryExpression primaryExpression = primarySuffix.getFirstParentOfType(ASTPrimaryExpression.class); - ASTPrimaryPrefix varPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); - if (prefixWithNoModifiers(varPrefix)) { - return varPrefix.getFirstDescendantOfType(ASTClassOrInterfaceType.class); - } - String varName = getVariableNameBySuffix(primaryExpression); - return varName != null ? getTypeOfVariableByName(varName, primaryExpression) : null; - } - - private boolean prefixWithNoModifiers(ASTPrimaryPrefix primaryPrefix) { - return !primaryPrefix.usesSuperModifier() && !primaryPrefix.usesThisModifier(); - } - - private String getVariableNameBySuffix(ASTPrimaryExpression primaryExpression) { - ASTPrimarySuffix varSuffix = primaryExpression - .getFirstChildOfType(ASTPrimarySuffix.class); - return varSuffix.getImage(); - } - - private ASTClassOrInterfaceType getTypeOfVariableByName(String varName, ASTPrimaryExpression expr) { - ASTClassOrInterfaceBody classBody = expr.getFirstParentOfType(ASTClassOrInterfaceBody.class); - List varDeclarators = classBody.findDescendantsOfType(ASTVariableDeclarator.class); - for (ASTVariableDeclarator varDeclarator : varDeclarators) { - if (varDeclarator.getName().equals(varName)) { - return varDeclarator.getFirstDescendantOfType(ASTClassOrInterfaceType.class); - } + public Object visit(ASTMethodCall call, Object data) { + if ((TypeTestUtil.isA(Collection.class, call.getQualifier()) + || TypeTestUtil.isA(Map.class, call.getQualifier())) + && isSizeZeroCheck(call)) { + addViolation(data, call); } return null; } - private ASTClassOrInterfaceType getTypeOfMethodCall(ASTPrimarySuffix node) { - ASTClassOrInterfaceType type = null; - ASTName methodName = node.getParent().getFirstChildOfType(ASTPrimaryPrefix.class) - .getFirstChildOfType(ASTName.class); - if (methodName != null) { - ClassScope classScope = node.getScope().getEnclosingScope(ClassScope.class); - Map> methods = classScope.getMethodDeclarations(); - for (Map.Entry> e : methods.entrySet()) { - if (e.getKey().getName().equals(methodName.getImage())) { - type = e.getKey().getNode().getFirstParentOfType(ASTMethodDeclaration.class) - .getFirstChildOfType(ASTResultType.class) - .getFirstDescendantOfType(ASTClassOrInterfaceType.class); - break; - } - } - } - return type; + private static boolean isSizeZeroCheck(ASTMethodCall call) { + return call.getMethodName().equals("size") + && call.getArguments().size() == 0 + && JavaRuleUtil.isZeroChecked(call); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java new file mode 100644 index 0000000000..e8c0007305 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java @@ -0,0 +1,69 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.internal; + +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; +import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral; +import net.sourceforge.pmd.lang.java.ast.BinaryOp; +import net.sourceforge.pmd.lang.java.ast.JavaNode; + +public final class JavaRuleUtil { + + private JavaRuleUtil() { + // utility class + } + + + /** + * Return true if the given expression is enclosed in a zero check. + * The expression must evaluate to a natural number (ie >= 0), so that + * {@code e < 1} actually means {@code e == 0}. + * + * @param e Expression + */ + public static boolean isZeroChecked(ASTExpression e) { + JavaNode parent = e.getParent(); + if (parent instanceof ASTInfixExpression) { + BinaryOp op = ((ASTInfixExpression) parent).getOperator(); + int checkLiteralAtIdx = 1 - e.getIndexInParent(); + JavaNode comparand = parent.getChild(checkLiteralAtIdx); + int expectedValue; + if (op == BinaryOp.NE || op == BinaryOp.EQ) { + // e == 0, e != 0, symmetric + expectedValue = 0; + } else if (op == BinaryOp.LT || op == BinaryOp.GE) { + // e < 1 + // 0 < e + // e >= 1 (e != 0) + // 1 >= e (e == 0 || e == 1) + // 0 >= e (e == 0) + // e >= 0 (true) + expectedValue = checkLiteralAtIdx; + } else if (op == BinaryOp.GT || op == BinaryOp.LE) { + // 1 > e + // e > 0 + + // 1 <= e (e != 0) + // e <= 1 (e == 0 || e == 1) + // e <= 0 (e == 0) + // 0 <= e (true) + expectedValue = 1 - checkLiteralAtIdx; + } else { + return false; + } + + return isIntLit(comparand, expectedValue); + } + return false; + } + + private static boolean isIntLit(JavaNode e, int value) { + if (e instanceof ASTNumericLiteral) { + return ((ASTNumericLiteral) e).getValueAsInt() == value; + } + return false; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java index db33f3af3d..9ad8abb92a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java @@ -5,11 +5,9 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; -import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral; -import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** @@ -62,22 +60,7 @@ public class InefficientEmptyStringCheckRule extends AbstractJavaRulechainRule { private static boolean isLengthZeroCheck(ASTMethodCall call) { return call.getMethodName().equals("length") && call.getArguments().size() == 0 - && isZeroCheck(call.getParent(), 1 - call.getIndexInParent()); - } - - private static boolean isZeroCheck(JavaNode e, int checkLiteralAtIdx) { - if (e instanceof ASTInfixExpression) { - return ((ASTInfixExpression) e).getOperator().isEquality() - && isIntLit(e.getChild(checkLiteralAtIdx), 0); - } - return false; - } - - private static boolean isIntLit(JavaNode e, int value) { - if (e instanceof ASTNumericLiteral) { - return ((ASTNumericLiteral) e).getValueAsInt() == value; - } - return false; + && JavaRuleUtil.isZeroChecked(call); } private static boolean isTrimCall(ASTExpression expr) { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyTest.java index 3dc87e3f32..f941133e82 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseCollectionIsEmptyTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml index d4f085f0df..0576bea05d 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml @@ -7,8 +7,9 @@ fail, == 0 1 - 3 + 4 ok, isEmpty 0 fail, != 0 1 - 3 + 4 ok, !isEmpty 0 fail, != 0 1 - 3 + 4 ok, !isEmpty 0 fail, 0 == 1 - 3 + 4 fail, > 0 1 - 3 + 4 0){ @@ -133,6 +141,7 @@ public class Foo { ok, in expression 0 ok, in expression 0 5 25,28,31,34,37 #1304 UseCollectionIsEmpty false positive comparing to 1 0 c = new ArrayList(); From ecf998272192edd3a7e5443083085abff8f45dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 30 Oct 2020 09:48:10 +0100 Subject: [PATCH 075/299] Update designer bindings --- .../java/internal/JavaDesignerBindings.java | 64 +++++++++++-------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaDesignerBindings.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaDesignerBindings.java index 956646196e..a4adccd5ea 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaDesignerBindings.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaDesignerBindings.java @@ -4,10 +4,14 @@ package net.sourceforge.pmd.lang.java.internal; -import static net.sourceforge.pmd.util.CollectionUtil.listOf; - +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; @@ -25,7 +29,9 @@ import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; import net.sourceforge.pmd.lang.java.ast.ASTRecordConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.ast.InvocationNode; +import net.sourceforge.pmd.lang.java.ast.JModifier; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.ast.JavaVisitorBase; import net.sourceforge.pmd.lang.java.ast.TypeNode; @@ -74,33 +80,39 @@ public final class JavaDesignerBindings extends DefaultDesignerBindings { @Override public Collection getAdditionalInfo(Node node) { + List info = new ArrayList<>(super.getAdditionalInfo(node)); if (node instanceof ASTLambdaExpression) { ASTLambdaExpression lambda = (ASTLambdaExpression) node; - return listOf( - new AdditionalInfo("Type: " + lambda.getTypeMirror()), - new AdditionalInfo("Function type: " + lambda.getFunctionalMethod()) - ); - } else if (node instanceof ASTMethodReference) { - ASTMethodReference lambda = (ASTMethodReference) node; - return listOf( - new AdditionalInfo("Type: " + lambda.getTypeMirror()), - new AdditionalInfo("Function type: " + lambda.getFunctionalMethod()), - new AdditionalInfo("CTDecl: " + lambda.getReferencedMethod()) - ); - } else if (node instanceof InvocationNode) { - InvocationNode invoc = (InvocationNode) node; - return listOf( - new AdditionalInfo("Type: " + invoc.getTypeMirror()), - new AdditionalInfo("Function: " + invoc.getMethodType()), - new AdditionalInfo("VarargsCall: " + invoc.getOverloadSelectionInfo().isVarargsCall()), - new AdditionalInfo("Unchecked: " + invoc.getOverloadSelectionInfo().needsUncheckedConversion()), - new AdditionalInfo("Failed: " + invoc.getOverloadSelectionInfo().isFailed()) - ); - } else if (node instanceof TypeNode) { - JTypeMirror typeMirror = ((TypeNode) node).getTypeMirror(); - return Collections.singletonList(new AdditionalInfo("Type: " + typeMirror)); + info.add(new AdditionalInfo("Function type: " + lambda.getFunctionalMethod())); } - return super.getAdditionalInfo(node); + if (node instanceof ASTMethodReference) { + ASTMethodReference lambda = (ASTMethodReference) node; + info.add(new AdditionalInfo("Function type: " + lambda.getFunctionalMethod())); + info.add(new AdditionalInfo("CTDecl: " + lambda.getReferencedMethod())); + } + if (node instanceof InvocationNode) { + InvocationNode invoc = (InvocationNode) node; + info.add(new AdditionalInfo("Function: " + invoc.getMethodType())); + info.add(new AdditionalInfo("VarargsCall: " + invoc.getOverloadSelectionInfo().isVarargsCall())); + info.add(new AdditionalInfo("Unchecked: " + invoc.getOverloadSelectionInfo().needsUncheckedConversion())); + info.add(new AdditionalInfo("Failed: " + invoc.getOverloadSelectionInfo().isFailed()); + } + if (node instanceof TypeNode) { + JTypeMirror typeMirror = ((TypeNode) node).getTypeMirror(); + info.add(new AdditionalInfo("Type: " + typeMirror)); + } + if (node instanceof AccessNode) { + String effective = formatModifierSet(((AccessNode) node).getModifiers().getEffectiveModifiers()); + String explicit = formatModifierSet(((AccessNode) node).getModifiers().getExplicitModifiers()); + info.add(new AdditionalInfo("pmd-java:modifiers(): " + effective)); + info.add(new AdditionalInfo("pmd-java:explicitModifiers(): " + explicit)); + } + return info; + } + + @NonNull + private String formatModifierSet(Set modifierSet) { + return modifierSet.stream().map(JModifier::toString).collect(Collectors.joining(", ", "(", ")")); } @Override From 8a6030acd94e3510dccf85feb6f112bf47ebea81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 30 Oct 2020 10:26:55 +0100 Subject: [PATCH 076/299] Move more utils to JavaRuleUtil --- .../codestyle/ClassNamingConventionsRule.java | 69 +----------------- .../lang/java/rule/internal/JavaRuleUtil.java | 71 +++++++++++++++++++ 2 files changed, 74 insertions(+), 66 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java index 5c5e496d58..db143dc85f 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java @@ -10,18 +10,11 @@ import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTInitializer; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTRecordDeclaration; -import net.sourceforge.pmd.lang.java.ast.AccessNode; -import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility; -import net.sourceforge.pmd.lang.java.ast.JModifier; import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; import net.sourceforge.pmd.lang.rule.RuleTargetSelector; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -56,69 +49,13 @@ public class ClassNamingConventionsRule extends AbstractNamingConventionRule descriptor) { - return isUtilityClass(node) ? "utility class" : PrettyPrintingUtil.kindName(node); + return JavaRuleUtil.isUtilityClass(node) ? "utility class" : PrettyPrintingUtil.kindName(node); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java index e8c0007305..c3789c0b50 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JavaRuleUtil.java @@ -4,11 +4,21 @@ package net.sourceforge.pmd.lang.java.rule.internal; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTBodyDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; +import net.sourceforge.pmd.lang.java.ast.ASTInitializer; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTNumericLiteral; +import net.sourceforge.pmd.lang.java.ast.AccessNode; +import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility; import net.sourceforge.pmd.lang.java.ast.BinaryOp; +import net.sourceforge.pmd.lang.java.ast.JModifier; import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; public final class JavaRuleUtil { @@ -66,4 +76,65 @@ public final class JavaRuleUtil { } return false; } + + /** + * Returns true if the node is a {@link ASTMethodDeclaration} that + * is a main method. + */ + public static boolean isMainMethod(JavaNode node) { + if (node instanceof ASTMethodDeclaration) { + ASTMethodDeclaration decl = (ASTMethodDeclaration) node; + + + return decl.hasModifiers(JModifier.PUBLIC, JModifier.STATIC) + && "main".equals(decl.getName()) + && decl.isVoid() + && decl.getArity() == 1 + && TypeTestUtil.isExactlyA(String[].class, decl.getFormalParameters().get(0)); + } + return false; + } + + /** + * Returns true if the node is a utility class, according to this + * custom definition. + */ + public static boolean isUtilityClass(ASTAnyTypeDeclaration node) { + if (node.isInterface() || node.isEnum()) { + return false; + } + + ASTClassOrInterfaceDeclaration classNode = (ASTClassOrInterfaceDeclaration) node; + + // A class with a superclass or interfaces should not be considered + if (classNode.getSuperClassTypeNode() != null + || !classNode.getSuperInterfaceTypeNodes().isEmpty()) { + return false; + } + + // A class without declarations shouldn't be reported + boolean hasAny = false; + + for (ASTBodyDeclaration declNode : classNode.getDeclarations()) { + if (declNode instanceof ASTFieldDeclaration + || declNode instanceof ASTMethodDeclaration) { + + hasAny = isNonPrivate(declNode) && !isMainMethod(declNode); + if (!((AccessNode) declNode).hasModifiers(JModifier.STATIC)) { + return false; + } + + } else if (declNode instanceof ASTInitializer) { + if (!((ASTInitializer) declNode).isStatic()) { + return false; + } + } + } + + return hasAny; + } + + private static boolean isNonPrivate(ASTBodyDeclaration decl) { + return ((AccessNode) decl).getVisibility() != Visibility.V_PRIVATE; + } } From 98f3656985cdd80f325a3ed9b8a47a5f39f0613d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 30 Oct 2020 10:36:54 +0100 Subject: [PATCH 077/299] Update ConfusingTernary --- .travis/all-java.xml | 2 +- .../java/internal/JavaDesignerBindings.java | 2 +- .../rule/codestyle/ConfusingTernaryRule.java | 109 ++++++------------ .../rule/codestyle/ConfusingTernaryTest.java | 1 - 4 files changed, 35 insertions(+), 79 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 3159631fae..255cd0bc0b 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -74,7 +74,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaDesignerBindings.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaDesignerBindings.java index a4adccd5ea..0a6650301b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaDesignerBindings.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaDesignerBindings.java @@ -95,7 +95,7 @@ public final class JavaDesignerBindings extends DefaultDesignerBindings { info.add(new AdditionalInfo("Function: " + invoc.getMethodType())); info.add(new AdditionalInfo("VarargsCall: " + invoc.getOverloadSelectionInfo().isVarargsCall())); info.add(new AdditionalInfo("Unchecked: " + invoc.getOverloadSelectionInfo().needsUncheckedConversion())); - info.add(new AdditionalInfo("Failed: " + invoc.getOverloadSelectionInfo().isFailed()); + info.add(new AdditionalInfo("Failed: " + invoc.getOverloadSelectionInfo().isFailed())); } if (node instanceof TypeNode) { JTypeMirror typeMirror = ((TypeNode) node).getTypeMirror(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryRule.java index b0ef3262e1..c6bd6593e4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryRule.java @@ -6,18 +6,14 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import static net.sourceforge.pmd.properties.PropertyFactory.booleanProperty; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTConditionalAndExpression; import net.sourceforge.pmd.lang.java.ast.ASTConditionalExpression; -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.ASTIfStatement; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpressionNotPlusMinus; -import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; +import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpression; +import net.sourceforge.pmd.lang.java.ast.BinaryOp; +import net.sourceforge.pmd.lang.java.ast.UnaryOp; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -54,29 +50,24 @@ import net.sourceforge.pmd.properties.PropertyDescriptor; * } *
*/ -public class ConfusingTernaryRule extends AbstractJavaRule { - private static PropertyDescriptor ignoreElseIfProperty = booleanProperty("ignoreElseIf").desc("Ignore conditions with an else-if case").defaultValue(false).build(); +public class ConfusingTernaryRule extends AbstractJavaRulechainRule { + + private static final PropertyDescriptor IGNORE_ELSE_IF = booleanProperty("ignoreElseIf").desc("Ignore conditions with an else-if case").defaultValue(false).build(); public ConfusingTernaryRule() { - super(); - definePropertyDescriptor(ignoreElseIfProperty); + super(ASTIfStatement.class, ASTConditionalExpression.class); + definePropertyDescriptor(IGNORE_ELSE_IF); } @Override public Object visit(ASTIfStatement node, Object data) { // look for "if (match) ..; else .." - if (node.getNumChildren() == 3) { - JavaNode inode = node.getChild(0); - if (inode instanceof ASTExpression && inode.getNumChildren() == 1) { - JavaNode jnode = inode.getChild(0); - if (isMatch(jnode)) { - - if (!getProperty(ignoreElseIfProperty) - || !(node.getChild(2).getChild(0) instanceof ASTIfStatement) - && !(node.getParent().getParent() instanceof ASTIfStatement)) { - addViolation(data, node); - } - } + if (node.getNumChildren() == 3 + && isMatch(node.getCondition())) { + if (!getProperty(IGNORE_ELSE_IF) + || !(node.getElseBranch() instanceof ASTIfStatement) + && !(node.getParent() instanceof ASTIfStatement)) { + addViolation(data, node); } } return super.visit(node, data); @@ -85,76 +76,42 @@ public class ConfusingTernaryRule extends AbstractJavaRule { @Override public Object visit(ASTConditionalExpression node, Object data) { // look for "match ? .. : .." - if (node.getNumChildren() > 0) { - JavaNode inode = node.getChild(0); - if (isMatch(inode)) { - addViolation(data, node); - } + if (isMatch(node.getCondition())) { + addViolation(data, node); } return super.visit(node, data); } // recursive! - private static boolean isMatch(JavaNode node) { - node = unwrapParentheses(node); + private static boolean isMatch(ASTExpression node) { return isUnaryNot(node) || isNotEquals(node) || isConditionalWithAllMatches(node); } - private static boolean isUnaryNot(Node node) { + private static boolean isUnaryNot(ASTExpression node) { // look for "!x" - return node instanceof ASTUnaryExpressionNotPlusMinus && "!".equals(node.getImage()); + return node instanceof ASTUnaryExpression + && ((ASTUnaryExpression) node).getOperator().equals(UnaryOp.NEGATION); } - private static boolean isNotEquals(Node node) { + private static boolean isNotEquals(ASTExpression node) { // look for "x != y" - return node instanceof ASTEqualityExpression && "!=".equals(node.getImage()); + return node instanceof ASTInfixExpression + && ((ASTInfixExpression) node).getOperator().equals(BinaryOp.NE); } - private static boolean isConditionalWithAllMatches(JavaNode node) { + private static boolean isConditionalWithAllMatches(ASTExpression node) { // look for "match && match" or "match || match" - if (!(node instanceof ASTConditionalAndExpression) && !(node instanceof ASTConditionalOrExpression)) { - return false; - } - int n = node.getNumChildren(); - if (n <= 0) { - return false; - } - for (int i = 0; i < n; i++) { - JavaNode inode = node.getChild(i); - // recurse! - if (!isMatch(inode)) { + if (node instanceof ASTInfixExpression) { + ASTInfixExpression infix = (ASTInfixExpression) node; + if (infix.getOperator() != BinaryOp.CONDITIONAL_AND + && infix.getOperator() != BinaryOp.CONDITIONAL_OR) { return false; } - } - // all match - return true; - } - /** - * Extracts the outermost node that is not a parenthesized - * expression. - * - * @deprecated This is internal API, because it will be removed in PMD 7. - * In PMD 7 there are no additional layers for parentheses in the Java tree. - */ - @Deprecated - public static JavaNode unwrapParentheses(final JavaNode top) { - JavaNode node = top; - // look for "(match)" - if (!(node instanceof ASTPrimaryExpression) || node.getNumChildren() != 1) { - return top; + return isMatch(infix.getLeftOperand()) + && isMatch(infix.getRightOperand()); } - node = node.getChild(0); - if (!(node instanceof ASTPrimaryPrefix) || node.getNumChildren() != 1) { - return top; - } - node = node.getChild(0); - if (!(node instanceof ASTExpression) || node.getNumChildren() != 1) { - return top; - } - node = node.getChild(0); - // recurse to unwrap another layer if possible - return unwrapParentheses(node); + return false; } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryTest.java index 8d978e150c..a023f70921 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/ConfusingTernaryTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ConfusingTernaryTest extends PmdRuleTst { // no additional unit tests } From f1d155770e0259f5c1bf0fc0aec20db97dfc8c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 30 Oct 2020 10:43:42 +0100 Subject: [PATCH 078/299] Update AtLeastOneConstructor --- .travis/all-java.xml | 2 +- .../codestyle/AtLeastOneConstructorRule.java | 38 ++++++------------- .../codestyle/AtLeastOneConstructorTest.java | 1 - 3 files changed, 13 insertions(+), 28 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 255cd0bc0b..ca18f5977e 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -65,7 +65,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorRule.java index ee06ee9951..0f4ea4caf1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorRule.java @@ -7,10 +7,11 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import java.util.Arrays; import java.util.Collection; +import net.sourceforge.pmd.lang.ast.NodeStream; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.AccessNode; import net.sourceforge.pmd.lang.java.rule.AbstractIgnoredAnnotationRule; import net.sourceforge.pmd.lang.java.rule.design.UseUtilityClassRule; @@ -39,33 +40,18 @@ public class AtLeastOneConstructorRule extends AbstractIgnoredAnnotationRule { @Override public Object visit(final ASTClassOrInterfaceDeclaration node, final Object data) { // Ignore interfaces / static classes / classes that have a constructor / classes ignored through annotations - if (node.isInterface() || node.isStatic() || node.hasDescendantOfType(ASTConstructorDeclaration.class) - || hasIgnoredAnnotation(node)) { + if (!node.isRegularClass() + || node.isStatic() + || node.getDeclarations().any(it -> it instanceof ASTConstructorDeclaration) + || hasIgnoredAnnotation(node)) { return data; } - boolean atLeastOneMember = false; - - // Do we have any non-static methods? - for (final ASTMethodDeclaration m : node.findDescendantsOfType(ASTMethodDeclaration.class)) { - if (!m.isStatic()) { - addViolation(data, node); - return data; - } - atLeastOneMember = true; - } - - // .. or fields? - for (final ASTFieldDeclaration f : node.findDescendantsOfType(ASTFieldDeclaration.class)) { - if (!f.isStatic()) { - addViolation(data, node); - return data; - } - atLeastOneMember = true; - } - - // Class has no declared members - if (!atLeastOneMember) { + NodeStream members = node.getDeclarations() + .filterIs(AccessNode.class) + .filterNot(it -> it instanceof ASTAnyTypeDeclaration); + if (members.isEmpty() || members.any(it -> !it.isStatic())) { + // Do we have any non-static members? addViolation(data, node); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorTest.java index e03e264b84..129fe92fa9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AtLeastOneConstructorTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AtLeastOneConstructorTest extends PmdRuleTst { // no additional unit tests } From 0a3b262740ed43c570fd9cad03e116cae5f30698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 30 Oct 2020 10:46:02 +0100 Subject: [PATCH 079/299] Update AvoidUsingNativeCode --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/codestyle.xml | 3 ++- .../pmd/lang/java/rule/codestyle/AvoidUsingNativeCodeTest.java | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index ca18f5977e..70ae201537 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -69,7 +69,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index e0495c5c8c..3bdd484764 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -145,7 +145,8 @@ and increases the maintenance burden. 2 - //Name[starts-with(@Image,'System.loadLibrary')] + //MethodCall[TypeExpression/ClassOrInterfaceType[pmd-java:typeIs('java.lang.System')] + and @MethodName = 'loadLibrary'] diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidUsingNativeCodeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidUsingNativeCodeTest.java index 2a7030ae70..8886751443 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidUsingNativeCodeTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/AvoidUsingNativeCodeTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidUsingNativeCodeTest extends PmdRuleTst { // no additional unit tests } From 9ffc8e0b972f6dea3be0381d509d97e7e7448c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 30 Oct 2020 10:48:21 +0100 Subject: [PATCH 080/299] Activate ForLoopVariableCount --- .travis/all-java.xml | 2 +- .../lang/java/rule/bestpractices/ForLoopVariableCountTest.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 70ae201537..9f7dccb138 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -25,7 +25,7 @@ - + diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ForLoopVariableCountTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ForLoopVariableCountTest.java index 79c05a8951..1bd0203640 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ForLoopVariableCountTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/ForLoopVariableCountTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class ForLoopVariableCountTest extends PmdRuleTst { // no additional unit tests } From fb0541420fcf35ff294602c857e60944c4f51a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 30 Oct 2020 11:01:46 +0100 Subject: [PATCH 081/299] Update LiteralsFirstInComparison --- .travis/all-java.xml | 2 +- .../LiteralsFirstInComparisonsRule.java | 198 +++--------------- .../LiteralsFirstInComparisonsTest.java | 1 - .../xml/LiteralsFirstInComparisons.xml | 3 +- 4 files changed, 33 insertions(+), 171 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 9f7dccb138..48497e3838 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -35,7 +35,7 @@ - + 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..7ad7f9c5f3 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 @@ -4,187 +4,49 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.util.List; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; +import static net.sourceforge.pmd.util.CollectionUtil.setOf; -import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTArguments; -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 java.util.Set; + +import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.java.ast.ASTExpression; -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.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -public class LiteralsFirstInComparisonsRule extends AbstractJavaRule { +public class LiteralsFirstInComparisonsRule extends AbstractJavaRulechainRule { - private static final String[] COMPARISON_OPS = {".equals", ".equalsIgnoreCase", ".compareTo", ".compareToIgnoreCase", ".contentEquals"}; + private static final Set STRING_COMPARISONS = + setOf("equalsIgnoreCase", + "compareTo", + "compareToIgnoreCase", + "contentEquals"); public LiteralsFirstInComparisonsRule() { - addRuleChainVisit(ASTPrimaryExpression.class); + super(ASTMethodCall.class); } @Override - public Object visit(ASTPrimaryExpression expression, Object data) { - if (violatesLiteralsFirstInComparisonsRule(expression)) { - addViolation(data, expression); + public Object visit(ASTMethodCall call, Object data) { + if (call.getMethodName().equals("equals") + // not an overload + && call.getMethodType().getFormalParameters().equals(listOf(call.getTypeSystem().OBJECT))) { + checkArgs((RuleContext) data, call); + } else if (STRING_COMPARISONS.contains(call.getMethodName()) + && call.getArguments().size() == 1 + && TypeTestUtil.isDeclaredInClass(String.class, call.getMethodType())) { + checkArgs((RuleContext) data, call); } return data; } - private boolean violatesLiteralsFirstInComparisonsRule(ASTPrimaryExpression expression) { - return !hasStringLiteralFirst(expression) && isNullableComparisonWithStringLiteral(expression); - } - - private boolean hasStringLiteralFirst(ASTPrimaryExpression expression) { - ASTPrimaryPrefix primaryPrefix = expression.getFirstChildOfType(ASTPrimaryPrefix.class); - ASTLiteral firstLiteral = primaryPrefix.getFirstDescendantOfType(ASTLiteral.class); - return firstLiteral != null && firstLiteral.isStringLiteral(); - } - - private boolean isNullableComparisonWithStringLiteral(ASTPrimaryExpression expression) { - String opName = getOperationName(expression); - ASTPrimarySuffix argsSuffix = getSuffixOfArguments(expression); - return opName != null && argsSuffix != null && isStringLiteralComparison(opName, argsSuffix) - && isNotWithinNullComparison(expression); - } - - private String getOperationName(ASTPrimaryExpression primaryExpression) { - return isMethodsChain(primaryExpression) ? getOperationNameBySuffix(primaryExpression) - : getOperationNameByPrefix(primaryExpression); - } - - private boolean isMethodsChain(ASTPrimaryExpression primaryExpression) { - return primaryExpression.getNumChildren() > 2; - } - - private String getOperationNameBySuffix(ASTPrimaryExpression primaryExpression) { - ASTPrimarySuffix opAsSuffix = getPrimarySuffixAtIndexFromEnd(primaryExpression, 1); - if (opAsSuffix != null) { - String opName = opAsSuffix.getImage(); // name of pattern "operation" - return "." + opName; + private void checkArgs(RuleContext ctx, ASTMethodCall call) { + ASTExpression arg = call.getArguments().get(0); + ASTExpression qualifier = call.getQualifier(); + if (!(qualifier instanceof ASTStringLiteral) && arg instanceof ASTStringLiteral) { + addViolation(ctx, call); } - return null; - } - - private String getOperationNameByPrefix(ASTPrimaryExpression primaryExpression) { - ASTPrimaryPrefix opAsPrefix = primaryExpression.getFirstChildOfType(ASTPrimaryPrefix.class); - if (opAsPrefix != null) { - ASTName opName = opAsPrefix.getFirstChildOfType(ASTName.class); // name of pattern "*.operation" - return opName != null ? opName.getImage() : null; - } - return null; - } - - private ASTPrimarySuffix getSuffixOfArguments(ASTPrimaryExpression primaryExpression) { - return getPrimarySuffixAtIndexFromEnd(primaryExpression, 0); - } - - private ASTPrimarySuffix getPrimarySuffixAtIndexFromEnd(ASTPrimaryExpression primaryExpression, int indexFromEnd) { - List primarySuffixes = primaryExpression.findChildrenOfType(ASTPrimarySuffix.class); - if (!primarySuffixes.isEmpty()) { - int suffixIndex = primarySuffixes.size() - 1 - indexFromEnd; - return primarySuffixes.get(suffixIndex); - } - return null; - } - - private boolean isStringLiteralComparison(String opName, ASTPrimarySuffix argsSuffix) { - return isComparisonOperation(opName) && isSingleStringLiteralArgument(argsSuffix); - } - - private boolean isComparisonOperation(String op) { - for (String comparisonOp : COMPARISON_OPS) { - if (op.endsWith(comparisonOp)) { - return true; - } - } - return false; - } - - /* - * This corresponds to the following XPath expression: - * (../PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral= true()]) - * and - * ( count(../PrimarySuffix/Arguments/ArgumentList/Expression) = 1 ) - */ - private boolean isSingleStringLiteralArgument(ASTPrimarySuffix primarySuffix) { - return isSingleArgumentSuffix(primarySuffix) && isStringLiteralFirstArgumentOfSuffix(primarySuffix); - } - - private boolean isSingleArgumentSuffix(ASTPrimarySuffix primarySuffix) { - return primarySuffix.getArgumentCount() == 1; - } - - private boolean isStringLiteralFirstArgumentOfSuffix(ASTPrimarySuffix primarySuffix) { - try { - JavaNode firstArg = getFirstArgument(primarySuffix); - return isStringLiteral(firstArg); - } catch (NullPointerException e) { - return false; - } - } - - private JavaNode getFirstArgument(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); - } - - private boolean isStringLiteral(JavaNode node) { - if (node instanceof ASTLiteral) { - ASTLiteral literal = (ASTLiteral) node; - return literal.isStringLiteral(); - } - return false; - } - - private boolean isNotWithinNullComparison(ASTPrimaryExpression node) { - return !isWithinNullComparison(node); - } - - /* - * Expression/ConditionalAndExpression//EqualityExpression(@Image='!=']//NullLiteral - * Expression/ConditionalOrExpression//EqualityExpression(@Image='==']//NullLiteral - */ - private boolean isWithinNullComparison(ASTPrimaryExpression node) { - for (ASTExpression parentExpr : node.getParentsOfType(ASTExpression.class)) { - if (isNullComparison(parentExpr)) { - return true; - } - } - return false; - } - - private boolean isNullComparison(ASTExpression expression) { - return isAndNotNullComparison(expression) || isOrNullComparison(expression); - } - - private boolean isAndNotNullComparison(ASTExpression expression) { - ASTConditionalAndExpression andExpression = expression - .getFirstChildOfType(ASTConditionalAndExpression.class); - return andExpression != null && hasEqualityExpressionWithNullLiteral(andExpression, "!="); - } - - private boolean isOrNullComparison(ASTExpression expression) { - ASTConditionalOrExpression orExpression = expression - .getFirstChildOfType(ASTConditionalOrExpression.class); - return orExpression != null && hasEqualityExpressionWithNullLiteral(orExpression, "=="); - } - - private boolean hasEqualityExpressionWithNullLiteral(JavaNode node, String equalityOp) { - ASTEqualityExpression equalityExpression = node.getFirstDescendantOfType(ASTEqualityExpression.class); - if (equalityExpression != null && equalityExpression.hasImageEqualTo(equalityOp)) { - return equalityExpression.hasDescendantOfType(ASTNullLiteral.class); - } - return false; } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsTest.java index 6977e9638d..afec19e09a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/LiteralsFirstInComparisonsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class LiteralsFirstInComparisonsTest extends PmdRuleTst { // no additional unit tests } 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..0c19b02d1c 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 @@ -280,10 +280,11 @@ public class Foo { bad, testing false negative at the end of a chain 1 From da78dbbac665c20a865bc319e072ac610dd0b165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 30 Oct 2020 11:04:18 +0100 Subject: [PATCH 082/299] Update EqualsNull --- .travis/all-java.xml | 2 +- .../main/resources/category/java/errorprone.xml | 14 +------------- .../lang/java/rule/errorprone/EqualsNullTest.java | 1 - 3 files changed, 2 insertions(+), 15 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 48497e3838..f0c91f0afc 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -224,7 +224,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 56d1a23b6a..9e9275a571 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1806,19 +1806,7 @@ Tests for null should not use the equals() method. The '==' operator should be u diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EqualsNullTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EqualsNullTest.java index 5dcb9eba9f..e869a5d609 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EqualsNullTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/EqualsNullTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class EqualsNullTest extends PmdRuleTst { // no additional unit tests } From 77ba1853debf3c4bcece42785bfa1cd6737978a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 30 Oct 2020 11:07:18 +0100 Subject: [PATCH 083/299] Update UseArrayListInsteadOfVector --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/performance.xml | 4 +--- .../rule/performance/UseArrayListInsteadOfVectorTest.java | 1 - .../java/rule/performance/xml/UseArrayListInsteadOfVector.xml | 3 +++ 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index f0c91f0afc..68f155d2d9 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -310,7 +310,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index c37cc870ad..f5bef2c92a 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -858,9 +858,7 @@ ArrayList is a much better Collection implementation than Vector if thread-safe diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseArrayListInsteadOfVectorTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseArrayListInsteadOfVectorTest.java index 8a1a78ed6d..296890c709 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseArrayListInsteadOfVectorTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseArrayListInsteadOfVectorTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseArrayListInsteadOfVectorTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseArrayListInsteadOfVector.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseArrayListInsteadOfVector.xml index f3870f345a..7556e7d23e 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseArrayListInsteadOfVector.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseArrayListInsteadOfVector.xml @@ -20,6 +20,7 @@ public class Bar { TEST1 1 TEST2 1 TEST3 1 Date: Fri, 30 Oct 2020 11:18:37 +0100 Subject: [PATCH 084/299] Update UseStringBufferLength --- .travis/all-java.xml | 2 +- .../UseStringBufferLengthRule.java | 115 +++--------------- .../UseStringBufferLengthTest.java | 1 - .../performance/xml/UseStringBufferLength.xml | 3 +- 4 files changed, 22 insertions(+), 99 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 68f155d2d9..6c52d53363 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -315,7 +315,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java index 7b449d59d7..ed43dae949 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java @@ -4,18 +4,10 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTLiteral; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; -import net.sourceforge.pmd.lang.symboltable.NameDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** * This rule finds places where StringBuffer.toString() is called just to see if @@ -34,99 +26,30 @@ import net.sourceforge.pmd.lang.symboltable.NameDeclaration; * @author acaplan * @author Philip Graf */ -public class UseStringBufferLengthRule extends AbstractJavaRule { +public class UseStringBufferLengthRule extends AbstractJavaRulechainRule { - // FIXME Need to remove this somehow. - /* - * Specifically, we need a symbol tree that can be traversed downwards, so - * that instead of visiting each name and then visiting the declaration for - * that name, we should visit all the declarations and check their usages. - * With that in place, this rule would be reduced to: - find all - * StringBuffer declarations - check each usage - flag those that involve - * variable.toString() - */ - private Set alreadySeen = new HashSet<>(); - - @Override - public Object visit(ASTMethodDeclaration acu, Object data) { - alreadySeen.clear(); - return super.visit(acu, data); + public UseStringBufferLengthRule() { + super(ASTMethodCall.class); } @Override - public Object visit(ASTName decl, Object data) { - if (!decl.getImage().endsWith("toString")) { - return data; + public Object visit(ASTMethodCall call, Object data) { + if (call.getMethodName().equals("toString") + && call.getArguments().size() == 0 + && TypeTestUtil.isA(CharSequence.class, call.getQualifier()) + && !TypeTestUtil.isA(String.class, call.getQualifier()) + && isLengthCall(call.getParent())) { + addViolation(data, call.getParent()); } - NameDeclaration nd = decl.getNameDeclaration(); - if (nd == null) { - return data; - } - if (alreadySeen.contains(nd) || !(nd instanceof VariableNameDeclaration) - || !ConsecutiveLiteralAppendsRule.isStringBuilderOrBuffer(((VariableNameDeclaration) nd).getDeclaratorId())) { - return data; - } - alreadySeen.add(nd); - - if (isViolation(decl)) { - addViolation(data, decl); - } - return data; } - /** - * Returns true for the following violations: - * - *
-     * StringBuffer sb = new StringBuffer("some string");
-     * if (sb.toString().equals("")) {
-     *     // this is a violation
-     * }
-     * if (sb.toString().length() == 0) {
-     *     // this is a violation
-     * }
-     * if (sb.length() == 0) {
-     *     // this is ok
-     * }
-     * 
- */ - private boolean isViolation(ASTName decl) { - // the (grand)parent of a violation has four children - Node parent = decl.getParent().getParent(); - if (parent.getNumChildren() == 4) { - // 1. child: sb.toString where sb is a VariableNameDeclaration for a - // StringBuffer or StringBuilder - if (parent.getChild(0).getFirstChildOfType(ASTName.class).getImage().endsWith(".toString")) { - // 2. child: the arguments of toString - // no need to check as both StringBuffer and StringBuilder only - // have one toString method - // 3. child: equals or length, 4. child: their arguments - return isEqualsViolation(parent) || isLengthViolation(parent); - } + private boolean isLengthCall(JavaNode node) { + if (node instanceof ASTMethodCall) { + ASTMethodCall call = (ASTMethodCall) node; + return call.getMethodName().equals("length") + && call.getArguments().size() == 0; } return false; } - - private boolean isEqualsViolation(Node parent) { - // 3. child: equals - if (parent.getChild(2).hasImageEqualTo("equals")) { - // 4. child: the arguments of equals, there must be exactly one and - // it must be "" - List argList = parent.getChild(3).findDescendantsOfType(ASTArgumentList.class); - if (argList.size() == 1) { - List literals = argList.get(0).findDescendantsOfType(ASTLiteral.class); - return literals.size() == 1 && literals.get(0).hasImageEqualTo("\"\""); - } - } - return false; - } - - private boolean isLengthViolation(Node parent) { - // 3. child: length - return parent.getChild(2).hasImageEqualTo("length"); - // 4. child: the arguments of length - // no need to check as String has only one length method - } - } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthTest.java index c09449b7e7..1d7ebb58d6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.performance; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseStringBufferLengthTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseStringBufferLength.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseStringBufferLength.xml index 5db78c58c3..e2e4029030 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseStringBufferLength.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UseStringBufferLength.xml @@ -28,8 +28,9 @@ public class Foo { + StringBuffer.toString.equals(""), bad - 2 + 0 Date: Sun, 1 Nov 2020 01:17:11 +0100 Subject: [PATCH 085/299] Use new attributes --- pmd-java/src/main/resources/category/java/codestyle.xml | 2 +- pmd-java/src/main/resources/category/java/performance.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index 3bdd484764..c89f53d28e 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -188,7 +188,7 @@ prefix for these methods. //MethodDeclaration [starts-with(@Name, 'get')] [@Arity = 0 or $checkParameterizedMethods = true()] - [ PrimitiveType[@Kind = 'boolean'] and not(pmd-java:hasAnnotation('java.lang.Override')) ] + [ PrimitiveType[@Kind = 'boolean'] and @Overridden = false() ] ]]> diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index f5bef2c92a..7dc53dac5a 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -285,8 +285,8 @@ adverse impacts on performance. //PrimitiveType[@Kind = 'short'][ parent::FieldDeclaration or parent::LocalVariableDeclaration - or parent::FormalParameter and ../../../self::MethodDeclaration[not(pmd-java:hasAnnotation('java.lang.Override'))] - or parent::MethodDeclaration[not(pmd-java:hasAnnotation('java.lang.Override'))] + or parent::FormalParameter and ../../../self::MethodDeclaration[@Overridden = false()] + or parent::MethodDeclaration[@Overridden = false()] ] ]]> From 3e17f9c1205454772a7325e4284a1aa2ad584829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 1 Nov 2020 01:25:17 +0100 Subject: [PATCH 086/299] Update SimplifyBooleanExpressions --- .travis/all-java.xml | 2 +- pmd-java/src/main/resources/category/java/design.xml | 3 +-- .../lang/java/rule/design/SimplifyBooleanExpressionsTest.java | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 6c52d53363..9e3e00ea79 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -152,7 +152,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/design.xml b/pmd-java/src/main/resources/category/java/design.xml index 8b743427ae..411be40ab8 100644 --- a/pmd-java/src/main/resources/category/java/design.xml +++ b/pmd-java/src/main/resources/category/java/design.xml @@ -1144,8 +1144,7 @@ Avoid unnecessary comparisons in boolean expressions, they serve no purpose and diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyBooleanExpressionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyBooleanExpressionsTest.java index 3f30017e80..ef90b6a888 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyBooleanExpressionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/design/SimplifyBooleanExpressionsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.design; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SimplifyBooleanExpressionsTest extends PmdRuleTst { // no additional unit tests } From 5fd0c07e083796c10662d30dc07cf6afc44bcd42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 6 Aug 2020 23:23:37 +0200 Subject: [PATCH 087/299] Update CompareObjectsWithEquals --- .travis/all-java.xml | 2 +- .../CompareObjectsWithEqualsRule.java | 110 +++++------------- .../xml/CompareObjectsWithEquals.xml | 4 +- 3 files changed, 29 insertions(+), 87 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 9e3e00ea79..2c8c405e94 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -202,7 +202,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java index 4b77168b9b..d2db742bf4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java @@ -4,110 +4,52 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; -import net.sourceforge.pmd.lang.java.ast.ASTInitializer; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTReferenceType; +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.java.ast.ASTArrayAllocation; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; +import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; public class CompareObjectsWithEqualsRule extends AbstractJavaRule { - private boolean hasName(Node n) { - return n.getNumChildren() > 0 && n.getChild(0) instanceof ASTName; + + @Override + protected @NonNull RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(ASTInfixExpression.class); } - /** - * Indicate whether this node is allocating a new object. - * - * @param n - * node that might be allocating a new object - * @return true if child 0 is an AllocationExpression - */ - private boolean isAllocation(Node n) { - return n.getNumChildren() > 0 && n.getChild(0) instanceof ASTAllocationExpression - && n.getParent().getNumChildren() == 1; + /** Indicate whether this node is allocating a new object. */ + private boolean isAllocation(ASTExpression n) { + return n instanceof ASTConstructorCall || n instanceof ASTArrayAllocation; } @Override - public Object visit(ASTEqualityExpression node, Object data) { - Node c0 = node.getChild(0).getChild(0); - Node c1 = node.getChild(1).getChild(0); + public Object visit(ASTInfixExpression node, Object data) { + if (!node.getOperator().isEquality()) { + return data; + } + ASTExpression left = node.getLeftOperand(); + ASTExpression right = node.getRightOperand(); // If either side is allocating a new object, there's no way an // equals expression is correct - if (isAllocation(c0) || isAllocation(c1)) { + if (isAllocation(left) || isAllocation(right)) { addViolation(data, node); return data; } - // skip if either child is not a simple name - if (!hasName(c0) || !hasName(c1)) { + if (left.getTypeMirror().isPrimitive() || right.getTypeMirror().isPrimitive() + || TypeHelper.isA(left, Enum.class) || TypeHelper.isA(right, Enum.class) + || TypeHelper.isA(left, Class.class) || TypeHelper.isA(right, Class.class)) { return data; } - - // skip if either is a qualified name - if (isQualifiedName(c0.getChild(0).getImage()) || isQualifiedName(c1.getChild(0).getImage())) { - return data; - } - - // skip if either is part of a qualified name - if (isPartOfQualifiedName(node.getChild(0)) || isPartOfQualifiedName(node.getChild(1))) { - return data; - } - - // skip static initializers... missing some cases here - if (!node.getParentsOfType(ASTInitializer.class).isEmpty()) { - return data; - } - - ASTName n0 = (ASTName) c0.getChild(0); - ASTName n1 = (ASTName) c1.getChild(0); - - if (n0.getNameDeclaration() instanceof VariableNameDeclaration - && n1.getNameDeclaration() instanceof VariableNameDeclaration) { - VariableNameDeclaration nd0 = (VariableNameDeclaration) n0.getNameDeclaration(); - VariableNameDeclaration nd1 = (VariableNameDeclaration) n1.getNameDeclaration(); - - // skip array dereferences... this misses some cases - // FIXME catch comparisons btwn array elements of reference types - if (nd0.isArray() || nd1.isArray()) { - return data; - } - - if (nd0.isReferenceType() && nd1.isReferenceType()) { - ASTReferenceType type0 = ((Node) nd0.getAccessNodeParent()) - .getFirstDescendantOfType(ASTReferenceType.class); - ASTReferenceType type1 = ((Node) nd1.getAccessNodeParent()) - .getFirstDescendantOfType(ASTReferenceType.class); - // skip, if it is an enum - if (type0.getType() != null && type0.getType().equals(type1.getType()) - // It may be a custom enum class or an explicit Enum class usage - && (type0.getType().isEnum() || type0.getType() == java.lang.Enum.class)) { - return data; - } - - addViolation(data, node); - } - } + addViolation(data, node); return data; } - /** - * Checks whether the given node contains a qualified name, consisting of - * one ASTPrimaryPrefix and one or more ASTPrimarySuffix nodes. - * - * @param node - * the node - * @return true if it is a qualified name - */ - private boolean isPartOfQualifiedName(Node node) { - return node.getChild(0) instanceof ASTPrimaryPrefix - && !node.findChildrenOfType(ASTPrimarySuffix.class).isEmpty(); - } } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index 987dbbbd85..632de9db3c 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -41,8 +41,8 @@ public class Foo { - missed hit - qualified names. that's ok, we can't resolve the types yet, so better to skip this for now - 0 + Hits it + 1 Date: Thu, 6 Aug 2020 23:12:38 +0200 Subject: [PATCH 088/299] Update CloneMethodMustImplementCloneable --- .travis/all-java.xml | 2 +- ...CloneMethodMustImplementCloneableRule.java | 156 +++--------------- 2 files changed, 28 insertions(+), 130 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 2c8c405e94..4211504152 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -198,7 +198,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java index 495e9bf31c..172644195b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java @@ -4,22 +4,16 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import org.checkerframework.checker.nullness.qual.NonNull; -import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTBlock; -import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; -import net.sourceforge.pmd.lang.java.ast.ASTExtendsList; -import net.sourceforge.pmd.lang.java.ast.ASTImplementsList; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclarator; +import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; /** * The method clone() should only be implemented if the class implements the @@ -33,136 +27,40 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; public class CloneMethodMustImplementCloneableRule extends AbstractJavaRule { @Override - public Object visit(final ASTClassOrInterfaceDeclaration node, final Object data) { - if (extendsOrImplementsCloneable(node)) { - return data; - } - return super.visit(node, data); - } - - private boolean extendsOrImplementsCloneable(final ASTClassOrInterfaceDeclaration node) { - if (node.getType() != null) { - return Cloneable.class.isAssignableFrom(node.getType()); - } - - // From this point on, this is a best effort, the auxclasspath is incomplete. - - // TODO : Should we really care about this? - // Shouldn't the type resolver / symbol table report missing classes and the user - // know results are dependent on running under proper arguments? - final ASTImplementsList impl = node.getFirstChildOfType(ASTImplementsList.class); - if (impl != null) { - for (int ix = 0; ix < impl.getNumChildren(); ix++) { - final Node child = impl.getChild(ix); - - if (child.getClass() != ASTClassOrInterfaceType.class) { - continue; - } - - final ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) child; - if (type.getType() == null) { - if ("Cloneable".equals(type.getImage())) { - return true; - } - } else if (Cloneable.class.isAssignableFrom(type.getType())) { - return true; - } - } - } - - if (node.getNumChildren() != 0 && node.getChild(0) instanceof ASTExtendsList) { - final ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) node.getChild(0).getChild(0); - final Class clazz = type.getType(); - if (clazz != null) { - return Cloneable.class.isAssignableFrom(clazz); - } - } - - return false; + protected @NonNull RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(ASTMethodDeclaration.class); } @Override public Object visit(final ASTMethodDeclaration node, final Object data) { - // Is this a clone method? - final ASTMethodDeclarator methodDeclarator = node.getFirstChildOfType(ASTMethodDeclarator.class); - if (!isCloneMethod(methodDeclarator)) { + if (node.isAbstract() || !isCloneMethod(node)) { + return data; + } else if (justThrowsCloneNotSupported(node)) { return data; } - // Is the clone method just throwing CloneNotSupportedException? - final ASTClassOrInterfaceDeclaration classOrInterface = node.getFirstParentOfType(ASTClassOrInterfaceDeclaration.class); - if (classOrInterface != null //Don't analyze enums, which cannot subclass clone() - && (node.isFinal() || classOrInterface.isFinal())) { - if (node.findDescendantsOfType(ASTBlock.class).size() == 1) { - final List blocks = node.findDescendantsOfType(ASTBlockStatement.class); - if (blocks.size() == 1) { - final ASTBlockStatement block = blocks.get(0); - final ASTClassOrInterfaceType type = block.getFirstDescendantOfType(ASTClassOrInterfaceType.class); - if (type != null && type.getType() != null && type.getNthParent(9).equals(node) - && type.getType().equals(CloneNotSupportedException.class)) { - return data; - } else if (type != null && type.getType() == null - && "CloneNotSupportedException".equals(type.getImage())) { - return data; - } - } - } + ASTAnyTypeDeclaration type = node.getEnclosingType(); + if (type instanceof ASTClassOrInterfaceDeclaration && !TypeHelper.isA(type, Cloneable.class)) { + // Nothing can save us now + addViolation(data, node); } - - // TODO : Should we really care about this? It can only happen with an incomplete auxclasspath - if (classOrInterface != null && classOrInterface.getType() == null) { - // Now check other whether implemented or extended classes are defined inside the same file - final Set classesNames = determineTopLevelCloneableClasses(classOrInterface); - - final ASTImplementsList implementsList = classOrInterface.getFirstChildOfType(ASTImplementsList.class); - if (implementsList != null) { - final List types = implementsList.findChildrenOfType(ASTClassOrInterfaceType.class); - for (final ASTClassOrInterfaceType t : types) { - if (classesNames.contains(t.getImage())) { - return data; - } - } - } - - final ASTExtendsList extendsList = classOrInterface.getFirstChildOfType(ASTExtendsList.class); - if (extendsList != null) { - final ASTClassOrInterfaceType type = extendsList.getFirstChildOfType(ASTClassOrInterfaceType.class); - if (classesNames.contains(type.getImage())) { - return data; - } - } - } - - // Nothing can save us now - addViolation(data, node); return data; } - /** - * Determines all the class/interface declarations inside this compilation - * unit, which implement Cloneable - * - * @param currentClass - * the node of the class, that is currently analyzed (inside this - * compilation unit) - * @return a Set of class/interface names - */ - private Set determineTopLevelCloneableClasses(final ASTClassOrInterfaceDeclaration currentClass) { - final List classes = currentClass.getFirstParentOfType(ASTCompilationUnit.class) - .findDescendantsOfType(ASTClassOrInterfaceDeclaration.class); - final Set classesNames = new HashSet<>(); - for (final ASTClassOrInterfaceDeclaration c : classes) { - if (!Objects.equals(c, currentClass) && extendsOrImplementsCloneable(c)) { - classesNames.add(c.getImage()); - } - } - return classesNames; - } - - public boolean isCloneMethod(final ASTMethodDeclarator method) { - if (!"clone".equals(method.getImage())) { + private static boolean justThrowsCloneNotSupported(ASTMethodDeclaration decl) { + ASTBlock body = decl.getBody(); + if (body.size() != 1) { return false; } - return method.getParameterCount() == 0; + return body.getChild(0) + .asStream() + .filterIs(ASTThrowStatement.class) + .map(ASTThrowStatement::getExpr) + .filter(it -> TypeHelper.isA(it, CloneNotSupportedException.class)) + .nonEmpty(); + } + + private static boolean isCloneMethod(final ASTMethodDeclaration method) { + return "clone".equals(method.getName()) && method.getFormalParameters().size() == 0; } } From 0ac60ebd1968788b2e81c9c5a03f9d18ad7646f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 1 Nov 2020 21:22:24 +0100 Subject: [PATCH 089/299] Fixes --- ...CloneMethodMustImplementCloneableRule.java | 26 ++++++++--------- .../CompareObjectsWithEqualsRule.java | 28 +++++++++---------- ...CloneMethodMustImplementCloneableTest.java | 1 - .../CompareObjectsWithEqualsTest.java | 1 - 4 files changed, 24 insertions(+), 32 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java index 172644195b..cf65b80577 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java @@ -4,16 +4,13 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import org.checkerframework.checker.nullness.qual.NonNull; - import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; -import net.sourceforge.pmd.lang.rule.RuleTargetSelector; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** * The method clone() should only be implemented if the class implements the @@ -24,31 +21,30 @@ import net.sourceforge.pmd.lang.rule.RuleTargetSelector; * * @author acaplan */ -public class CloneMethodMustImplementCloneableRule extends AbstractJavaRule { +public class CloneMethodMustImplementCloneableRule extends AbstractJavaRulechainRule { - @Override - protected @NonNull RuleTargetSelector buildTargetSelector() { - return RuleTargetSelector.forTypes(ASTMethodDeclaration.class); + public CloneMethodMustImplementCloneableRule() { + super(ASTMethodDeclaration.class); } @Override public Object visit(final ASTMethodDeclaration node, final Object data) { - if (node.isAbstract() || !isCloneMethod(node)) { + ASTBlock body = node.getBody(); + if (body == null || !isCloneMethod(node)) { return data; - } else if (justThrowsCloneNotSupported(node)) { + } else if (justThrowsCloneNotSupported(body)) { return data; } ASTAnyTypeDeclaration type = node.getEnclosingType(); - if (type instanceof ASTClassOrInterfaceDeclaration && !TypeHelper.isA(type, Cloneable.class)) { + if (type instanceof ASTClassOrInterfaceDeclaration && !TypeTestUtil.isA(Cloneable.class, type)) { // Nothing can save us now addViolation(data, node); } return data; } - private static boolean justThrowsCloneNotSupported(ASTMethodDeclaration decl) { - ASTBlock body = decl.getBody(); + private static boolean justThrowsCloneNotSupported(ASTBlock body) { if (body.size() != 1) { return false; } @@ -56,7 +52,7 @@ public class CloneMethodMustImplementCloneableRule extends AbstractJavaRule { .asStream() .filterIs(ASTThrowStatement.class) .map(ASTThrowStatement::getExpr) - .filter(it -> TypeHelper.isA(it, CloneNotSupportedException.class)) + .filter(it -> TypeTestUtil.isA(CloneNotSupportedException.class, it)) .nonEmpty(); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java index d2db742bf4..750ffbc866 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java @@ -4,22 +4,17 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import org.checkerframework.checker.nullness.qual.NonNull; - import net.sourceforge.pmd.lang.java.ast.ASTArrayAllocation; import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.typeresolution.TypeHelper; -import net.sourceforge.pmd.lang.rule.RuleTargetSelector; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -public class CompareObjectsWithEqualsRule extends AbstractJavaRule { +public class CompareObjectsWithEqualsRule extends AbstractJavaRulechainRule { - - @Override - protected @NonNull RuleTargetSelector buildTargetSelector() { - return RuleTargetSelector.forTypes(ASTInfixExpression.class); + public CompareObjectsWithEqualsRule() { + super(ASTInfixExpression.class); } /** Indicate whether this node is allocating a new object. */ @@ -42,14 +37,17 @@ public class CompareObjectsWithEqualsRule extends AbstractJavaRule { return data; } - if (left.getTypeMirror().isPrimitive() || right.getTypeMirror().isPrimitive() - || TypeHelper.isA(left, Enum.class) || TypeHelper.isA(right, Enum.class) - || TypeHelper.isA(left, Class.class) || TypeHelper.isA(right, Class.class)) { - return data; + if (!isIgnoredType(left) && !isIgnoredType(right)) { + addViolation(data, node); } - addViolation(data, node); return data; } + private boolean isIgnoredType(ASTExpression left) { + return left.getTypeMirror().isPrimitive() + || TypeTestUtil.isA(Enum.class, left) + || TypeTestUtil.isA(Class.class, left); + } + } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableTest.java index 0e9797fc46..f1f5744870 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class CloneMethodMustImplementCloneableTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsTest.java index 7fbce4fd5b..46b5b9b837 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class CompareObjectsWithEqualsTest extends PmdRuleTst { // no additional unit tests } From 5e07ccd0178e6a781a3a6c98c745167937ff5506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 1 Nov 2020 23:54:08 +0100 Subject: [PATCH 090/299] Update UnnecessaryConstructor --- .travis/all-java.xml | 2 +- .../codestyle/UnnecessaryConstructorRule.java | 109 ++++++++---------- .../codestyle/UnnecessaryConstructorTest.java | 1 - .../codestyle/xml/UnnecessaryConstructor.xml | 1 + 4 files changed, 52 insertions(+), 61 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 4211504152..aa49deb446 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -108,7 +108,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorRule.java index 37e420ee19..5f9df5df58 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorRule.java @@ -8,14 +8,18 @@ import java.util.Collection; import java.util.Collections; import java.util.List; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTExplicitConstructorInvocation; -import net.sourceforge.pmd.lang.java.ast.ASTNameList; +import net.sourceforge.pmd.lang.java.ast.ASTStatement; +import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility; import net.sourceforge.pmd.lang.java.rule.AbstractIgnoredAnnotationRule; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; /** * This rule detects when a constructor is not necessary; @@ -24,6 +28,11 @@ import net.sourceforge.pmd.lang.java.rule.AbstractIgnoredAnnotationRule; */ public class UnnecessaryConstructorRule extends AbstractIgnoredAnnotationRule { + @Override + protected @NonNull RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(ASTEnumDeclaration.class, ASTClassOrInterfaceDeclaration.class); + } + @Override protected Collection defaultSuppressionAnnotations() { return Collections.singletonList("javax.inject.Inject"); @@ -31,72 +40,54 @@ public class UnnecessaryConstructorRule extends AbstractIgnoredAnnotationRule { @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - - ASTConstructorDeclaration cons = node.getFirstDescendantOfType(ASTConstructorDeclaration.class); - if (isExplicitDefaultConstructor(node) - && haveSameAccessModifier(node, cons)) { - addViolation(data, cons); + if (node.isRegularClass()) { + checkClassOrEnum(node, data); } - - return super.visit(node, data); + return data; } @Override public Object visit(ASTEnumDeclaration node, Object data) { + checkClassOrEnum(node, data); + return data; + } - ASTConstructorDeclaration cons = node.getFirstDescendantOfType(ASTConstructorDeclaration.class); - if (isExplicitDefaultConstructor(node) && cons.isPrivate()) { - addViolation(data, cons); + private void checkClassOrEnum(ASTAnyTypeDeclaration node, Object data) { + List ctors = node.getDeclarations().filterIs(ASTConstructorDeclaration.class).take(2).toList(); + if (ctors.size() == 1 && isExplicitDefaultConstructor(node, ctors.get(0))) { + addViolation(data, ctors.get(0)); + } + } + + + private boolean isExplicitDefaultConstructor(ASTAnyTypeDeclaration declarator, ASTConstructorDeclaration ctor) { + return ctor.getArity() == 0 + && !hasIgnoredAnnotation(ctor) + && hasDefaultCtorVisibility(declarator, ctor) + && isEmptyBlock(ctor.getBody()) + && ctor.getThrowsList() == null; + } + + private boolean isEmptyBlock(ASTBlock body) { + if (body.size() == 0) { + return true; + } else if (body.size() == 1) { + ASTStatement stmt = body.get(0); + if (stmt instanceof ASTExplicitConstructorInvocation) { + ASTExplicitConstructorInvocation superCall = (ASTExplicitConstructorInvocation) stmt; + return superCall.isSuper() && superCall.getArgumentCount() == 0; + } } - return super.visit(node, data); + return false; } - /** - * Returns {@code true} if the node has only one {@link ASTConstructorDeclaration} - * child node and the constructor has empty body or simply invokes the superclass - * constructor with no arguments. - * - * @param node the node to check - */ - private boolean isExplicitDefaultConstructor(Node node) { - - List nodes - = node.findDescendantsOfType(ASTConstructorDeclaration.class); - - if (nodes.size() != 1) { - return false; + private boolean hasDefaultCtorVisibility(ASTAnyTypeDeclaration node, ASTConstructorDeclaration cons) { + if (node instanceof ASTClassOrInterfaceDeclaration) { + return node.getVisibility() == cons.getVisibility(); + } else if (node instanceof ASTEnumDeclaration) { + return cons.getVisibility() == Visibility.V_PRIVATE; } - - ASTConstructorDeclaration cdnode = nodes.get(0); - - return cdnode.getArity() == 0 && !hasIgnoredAnnotation(cdnode) - && !cdnode.hasDescendantOfType(ASTBlockStatement.class) && !cdnode.hasDescendantOfType(ASTNameList.class) - && hasDefaultConstructorInvocation(cdnode); - } - - /** - * Returns {@code true} if the constructor simply invokes superclass constructor - * with no arguments or doesn't invoke any constructor, otherwise {@code false}. - * - * @param cons the node to check - */ - private boolean hasDefaultConstructorInvocation(ASTConstructorDeclaration cons) { - ASTExplicitConstructorInvocation inv = cons.getFirstChildOfType(ASTExplicitConstructorInvocation.class); - return inv == null || inv.isSuper() && inv.getArgumentCount() == 0; - } - - /** - * Returns {@code true} if access modifier of constructor is same as class's, - * otherwise {@code false}. - * - * @param node the class declaration node - * @param cons the constructor declaration node - */ - private boolean haveSameAccessModifier(ASTClassOrInterfaceDeclaration node, ASTConstructorDeclaration cons) { - return node.isPrivate() && cons.isPrivate() - || node.isProtected() && cons.isProtected() - || node.isPublic() && cons.isPublic() - || node.isPackagePrivate() && cons.isPackagePrivate(); + return false; } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorTest.java index cf2558d8f4..b2cf45becf 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UnnecessaryConstructorTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryConstructor.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryConstructor.xml index 92303ef71b..3d14e79550 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryConstructor.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UnnecessaryConstructor.xml @@ -153,6 +153,7 @@ public class Foo { Issue #1003: UnnecessaryConstructor triggered on required empty constructor (Dagger @Inject) 0 Date: Mon, 2 Nov 2020 00:10:53 +0100 Subject: [PATCH 091/299] Fix SimplifyStartsWith --- pmd-java/src/main/resources/category/java/performance.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 7dc53dac5a..973a604ed1 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -684,7 +684,7 @@ ensure that the string is not empty by making an additional check first. From 097533479f9a72beca96c632e0266a6c1f2a59c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 00:20:40 +0100 Subject: [PATCH 092/299] Update AccessorClassGeneration --- .travis/all-java.xml | 2 +- .../AccessorClassGenerationRule.java | 83 ++++--------------- .../AccessorClassGenerationTest.java | 1 - 3 files changed, 18 insertions(+), 68 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index aa49deb446..87abe30633 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -10,7 +10,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java index 79a348e7a2..2dcd63991e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java @@ -4,18 +4,11 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.lang.reflect.Modifier; +import java.util.Objects; -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTArguments; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.ClassScope; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; /** * 1. Note all private constructors. 2. Note all instantiations from outside of @@ -32,67 +25,25 @@ import net.sourceforge.pmd.lang.java.symboltable.ClassScope; * @author Romain PELISSE, belaran@gmail.com, patch bug#1807370 * @author Juan Martin Sotuyo Dodero (juansotuyo@gmail.com), complete rewrite */ -public class AccessorClassGenerationRule extends AbstractJavaRule { - - private Map> privateConstructors = new HashMap<>(); +public class AccessorClassGenerationRule extends AbstractJavaRulechainRule { public AccessorClassGenerationRule() { - super(); - /* - * Order is important. Visit constructors first to find the private - * ones, then visit allocations to find violations - */ - addRuleChainVisit(ASTConstructorDeclaration.class); - addRuleChainVisit(ASTAllocationExpression.class); + super(ASTConstructorCall.class); } @Override - public void end(final RuleContext ctx) { - super.end(ctx); - // Clean up all references to the AST - privateConstructors.clear(); - } - - @Override - public Object visit(final ASTConstructorDeclaration node, final Object data) { - if (node.isPrivate()) { - final String className = node.getParent().getParent().getParent().getImage(); - if (!privateConstructors.containsKey(className)) { - privateConstructors.put(className, new ArrayList()); - } - privateConstructors.get(className).add(node); - } - return data; - } - - @Override - public Object visit(final ASTAllocationExpression node, final Object data) { - if (node.getChild(0) instanceof ASTClassOrInterfaceType) { // Ignore primitives - final ASTClassOrInterfaceType type = (ASTClassOrInterfaceType) node.getChild(0); - final List constructors = privateConstructors.get(type.getImage()); - - if (constructors != null) { - final ASTArguments callArguments = node.getFirstChildOfType(ASTArguments.class); - // Is this really a constructor call and not an array? - if (callArguments != null) { - final ClassScope enclosingScope = node.getScope().getEnclosingScope(ClassScope.class); - - for (final ASTConstructorDeclaration cd : constructors) { - // Are we within the same class scope? - if (cd.getScope().getEnclosingScope(ClassScope.class) == enclosingScope) { - break; - } - - if (cd.getArity() == callArguments.size()) { - // TODO : Check types - addViolation(data, node); - break; - } - } - } - } + public Object visit(ASTConstructorCall node, Object data) { + if (node.isAnonymousClass() + || Objects.equals(node.getTypeNode().getTypeMirror().getSymbol(), + node.getEnclosingType().getSymbol())) { + return null; } - return data; + // we're not in the same class + + if (Modifier.isPrivate(node.getMethodType().getModifiers())) { + addViolation(data, node); + } + return null; } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationTest.java index 481f730528..73faf421b8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AccessorClassGenerationTest extends PmdRuleTst { // no additional unit tests } From 98730760168b35e805cea060fcb59379318cd5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 00:52:08 +0100 Subject: [PATCH 093/299] Update AccessorMethodGeneration --- .travis/all-java.xml | 2 +- .../AccessorMethodGenerationRule.java | 197 +++++++----------- .../resources/category/java/bestpractices.xml | 2 +- .../AccessorMethodGenerationTest.java | 1 - .../xml/AccessorMethodGeneration.xml | 43 +++- 5 files changed, 114 insertions(+), 131 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 87abe30633..1c2dd06fce 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -11,7 +11,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java index 6917760242..e85866c9a9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java @@ -4,135 +4,92 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.lang.reflect.Modifier; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; 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.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclarator; -import net.sourceforge.pmd.lang.java.ast.AccessNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.AbstractJavaScope; -import net.sourceforge.pmd.lang.java.symboltable.ClassNameDeclaration; -import net.sourceforge.pmd.lang.java.symboltable.ClassScope; -import net.sourceforge.pmd.lang.java.symboltable.MethodNameDeclaration; -import net.sourceforge.pmd.lang.java.symboltable.SourceFileScope; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; +import net.sourceforge.pmd.lang.java.ast.ASTFieldAccess; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.symbols.JAccessibleElementSymbol; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; +import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; +import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; -public class AccessorMethodGenerationRule extends AbstractJavaRule { +public class AccessorMethodGenerationRule extends AbstractJavaRulechainRule { - private List cache = new ArrayList<>(); + private final Set reportedNodes = new HashSet<>(); - @Override - public Object visit(final ASTCompilationUnit node, final Object data) { - final SourceFileScope file = node.getScope().getEnclosingScope(SourceFileScope.class); - analyzeScope(file, data); - - return data; // Stop tree navigation - } - - private void analyzeScope(final AbstractJavaScope file, final Object data) { - for (final ClassNameDeclaration classDecl : file.getDeclarations(ClassNameDeclaration.class).keySet()) { - final ClassScope classScope = (ClassScope) classDecl.getScope(); - - // Check fields - for (final Map.Entry> varDecl : classScope.getVariableDeclarations().entrySet()) { - final ASTFieldDeclaration field = varDecl.getKey().getNode().getFirstParentOfType(ASTFieldDeclaration.class); - analyzeMember(field, varDecl.getValue(), classScope, data); - } - - // Check methods - for (final Map.Entry> methodDecl : classScope.getMethodDeclarations().entrySet()) { - final ASTMethodDeclaration method = methodDecl.getKey().getNode().getFirstParentOfType(ASTMethodDeclaration.class); - analyzeMember(method, methodDecl.getValue(), classScope, data); - } - - // Check inner classes - analyzeScope(classScope, data); - } - } - - public void analyzeMember(final AccessNode node, final List occurrences, - final ClassScope classScope, final Object data) { - if (!node.isPrivate()) { - return; - } - - if (node.isFinal()) { - for (final ASTVariableDeclarator varDecl: node.findChildrenOfType(ASTVariableDeclarator.class)) { - if (varDecl.hasInitializer()) { - ASTExpression varInit = varDecl.getInitializer(); - List initExpression = varInit.findDescendantsOfType(ASTExpression.class); - boolean isConstantExpression = true; - constantCheck: - for (ASTExpression exp: initExpression) { - List primaryExpressions = exp.findDescendantsOfType(ASTPrimaryExpression.class); - for (ASTPrimaryExpression expression: primaryExpressions) { - if (!isCompileTimeConstant(expression)) { - isConstantExpression = false; - break constantCheck; - } - } - } - if (isConstantExpression) { - cache.add(varDecl.getName()); - return; - } - } - } - } - - for (final NameOccurrence no : occurrences) { - ClassScope usedAtScope = no.getLocation().getScope().getEnclosingScope(ClassScope.class); - - // Are we within the same class that defines the field / method? - if (!classScope.equals(usedAtScope)) { - addViolation(data, no.getLocation()); - } - } - } - - public boolean isCompileTimeConstant(ASTPrimaryExpression expressions) { - // function call detected - List suffix = expressions.findDescendantsOfType(ASTPrimarySuffix.class); - if (!suffix.isEmpty()) { - return false; - } - - // single node expression - List nameNodes = expressions.findDescendantsOfType(ASTName.class); - List literalNodes = expressions.findDescendantsOfType(ASTLiteral.class); - if (nameNodes.size() + literalNodes.size() < 2) { - for (ASTName node: nameNodes) { - // TODO : use the symbol table to get the declaration of the referenced var and check it - if (!cache.contains(node.getImage())) { - return false; - } - } - return true; - } - - // multiple node expression - List subExpressions = expressions.findDescendantsOfType(ASTPrimaryExpression.class); - for (ASTPrimaryExpression exp: subExpressions) { - if (!isCompileTimeConstant(exp)) { - return false; - } - } - return true; + public AccessorMethodGenerationRule() { + super(ASTFieldAccess.class, ASTVariableAccess.class, ASTMethodCall.class); } @Override public void end(RuleContext ctx) { - cache.clear(); + super.end(ctx); + reportedNodes.clear(); + } + + @Override + public Object visit(ASTFieldAccess node, Object data) { + JFieldSymbol sym = node.getReferencedSym(); + if (sym != null && sym.getConstValue() == null) { + checkMemberAccess((RuleContext) data, node, sym); + } + return null; + } + + @Override + public Object visit(ASTVariableAccess node, Object data) { + JVariableSymbol sym = node.getReferencedSym(); + if (sym instanceof JFieldSymbol) { + JFieldSymbol fieldSym = (JFieldSymbol) sym; + if (((JFieldSymbol) sym).getConstValue() == null) { + checkMemberAccess((RuleContext) data, node, fieldSym); + } + } + return null; + } + + @Override + public Object visit(ASTMethodCall node, Object data) { + JMethodSymbol symbol = (JMethodSymbol) node.getMethodType().getSymbol(); + checkMemberAccess((RuleContext) data, node, symbol); + return null; + } + + private void checkMemberAccess(RuleContext data, ASTExpression refExpr, JAccessibleElementSymbol sym) { + if (Modifier.isPrivate(sym.getModifiers()) + && !Objects.equals(sym.getEnclosingClass(), + refExpr.getEnclosingType().getSymbol())) { + + JavaNode node = sym.tryGetNode(); + assert node != null : "Node should be in the same compilation unit"; + if (reportedNodes.add(node)) { + addViolation(data, node, new String[] {stripPackageName(refExpr.getEnclosingType().getSymbol())}); + } + } + } + + /** + * Returns the canonical name without the package name. Eg for a + * canonical name {@code com.github.Outer.Inner}, returns {@code Outer.Inner}. + */ + private static String stripPackageName(JClassSymbol symbol) { + String p = symbol.getPackageName(); + String canoName = symbol.getCanonicalName(); + if (canoName == null) { + return symbol.getSimpleName(); + } + if (p.isEmpty()) { + return canoName; + } + return canoName.substring(p.length() + 1);//+1 for the dot } } diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index d764c74eb2..0af60234db 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -67,7 +67,7 @@ public class Outer { language="java" since="5.5.4" maximumLanguageVersion="10" - message="Avoid autogenerated methods to access private fields and methods of inner / outer classes" + message="Consider giving this member package visibility to access it from {0} without a synthetic accessor method" class="net.sourceforge.pmd.lang.java.rule.bestpractices.AccessorMethodGenerationRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#accessormethodgeneration"> diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationTest.java index 6fbc2e6676..678eac6042 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AccessorMethodGenerationTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml index c6ffc8a927..5af1d09cd6 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml @@ -7,7 +7,10 @@ inner class accesses private field from outer class 1 - 8 + 2 + + Consider giving this member package visibility to access it from Foo.InnerClass without a synthetic accessor method + inner class accesses private field from outer class unqualified 1 - 8 + 2 outer class accesses private field from inner class 1 - 9 + 5 + + Consider giving this member package visibility to access it from Foo without a synthetic accessor method + inner class accesses private method of outer class, unqualified 1 - 4 + 8 inner class accesses private method of outer class, qualified 1 - 4 + 8 outer class accesses private method of inner class 1 - 8 + 3 java 10 + + check violations are not duplicated + 1 + 3 + + java 10 + + inner class accesses non-private methods of outer class 0 @@ -200,15 +227,15 @@ public class Foo implements Parcelable { #808 - [java] AccessorMethodGeneration false positives with compile time constants 4 - 25,26,27,28 + 8,9,10,11 Date: Mon, 2 Nov 2020 01:03:36 +0100 Subject: [PATCH 094/299] Share logic in both rules --- .../AccessorClassGenerationRule.java | 26 ++++++++++--------- .../AccessorMethodGenerationRule.java | 9 +++++-- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java index 2dcd63991e..25b35e7ed9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java @@ -4,10 +4,12 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.lang.reflect.Modifier; -import java.util.Objects; +import java.util.HashSet; +import java.util.Set; +import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; +import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; /** @@ -27,22 +29,22 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; */ public class AccessorClassGenerationRule extends AbstractJavaRulechainRule { + private final Set reportedNodes = new HashSet<>(); + public AccessorClassGenerationRule() { super(ASTConstructorCall.class); } + @Override + public void end(RuleContext ctx) { + super.end(ctx); + reportedNodes.clear(); + } + @Override public Object visit(ASTConstructorCall node, Object data) { - if (node.isAnonymousClass() - || Objects.equals(node.getTypeNode().getTypeMirror().getSymbol(), - node.getEnclosingType().getSymbol())) { - return null; - } - - // we're not in the same class - - if (Modifier.isPrivate(node.getMethodType().getModifiers())) { - addViolation(data, node); + if (!node.isAnonymousClass()) { + AccessorMethodGenerationRule.checkMemberAccess(this, (RuleContext) data, node, node.getMethodType().getSymbol(), this.reportedNodes); } return null; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java index e85866c9a9..a640aaecca 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java @@ -21,6 +21,7 @@ import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol; import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; +import net.sourceforge.pmd.lang.rule.AbstractRule; public class AccessorMethodGenerationRule extends AbstractJavaRulechainRule { @@ -64,7 +65,11 @@ public class AccessorMethodGenerationRule extends AbstractJavaRulechainRule { return null; } - private void checkMemberAccess(RuleContext data, ASTExpression refExpr, JAccessibleElementSymbol sym) { + private void checkMemberAccess(RuleContext data, ASTExpression node, JAccessibleElementSymbol symbol) { + checkMemberAccess(this, data, node, symbol, this.reportedNodes); + } + + static void checkMemberAccess(AbstractRule rule, RuleContext data, ASTExpression refExpr, JAccessibleElementSymbol sym, Set reportedNodes) { if (Modifier.isPrivate(sym.getModifiers()) && !Objects.equals(sym.getEnclosingClass(), refExpr.getEnclosingType().getSymbol())) { @@ -72,7 +77,7 @@ public class AccessorMethodGenerationRule extends AbstractJavaRulechainRule { JavaNode node = sym.tryGetNode(); assert node != null : "Node should be in the same compilation unit"; if (reportedNodes.add(node)) { - addViolation(data, node, new String[] {stripPackageName(refExpr.getEnclosingType().getSymbol())}); + rule.addViolation(data, node, new String[] {stripPackageName(refExpr.getEnclosingType().getSymbol())}); } } } From d905c5f0aae3618c069499dce379cc643d597dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 02:44:06 +0100 Subject: [PATCH 095/299] PMD warnings --- .../rule/bestpractices/AccessorMethodGenerationRule.java | 2 +- .../rule/bestpractices/LiteralsFirstInComparisonsRule.java | 2 +- .../java/rule/bestpractices/UseCollectionIsEmptyRule.java | 2 +- .../java/rule/performance/AppendCharacterWithCharRule.java | 2 +- .../rule/performance/InefficientEmptyStringCheckRule.java | 6 +++--- .../pmd/lang/java/rule/performance/UseIndexOfCharRule.java | 2 +- .../java/rule/performance/UseStringBufferLengthRule.java | 4 ++-- .../java/rule/performance/UselessStringValueOfRule.java | 2 +- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java index a640aaecca..34f188262b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java @@ -95,6 +95,6 @@ public class AccessorMethodGenerationRule extends AbstractJavaRulechainRule { if (p.isEmpty()) { return canoName; } - return canoName.substring(p.length() + 1);//+1 for the dot + return canoName.substring(p.length() + 1); //+1 for the dot } } 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 7ad7f9c5f3..38de34d0ce 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 @@ -30,7 +30,7 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRulechainRule { @Override public Object visit(ASTMethodCall call, Object data) { - if (call.getMethodName().equals("equals") + if ("equals".equals(call.getMethodName()) // not an overload && call.getMethodType().getFormalParameters().equals(listOf(call.getTypeSystem().OBJECT))) { checkArgs((RuleContext) data, call); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java index f313f76be2..9a3bc18c97 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseCollectionIsEmptyRule.java @@ -35,7 +35,7 @@ public class UseCollectionIsEmptyRule extends AbstractJavaRulechainRule { } private static boolean isSizeZeroCheck(ASTMethodCall call) { - return call.getMethodName().equals("size") + return "size".equals(call.getMethodName()) && call.getArguments().size() == 0 && JavaRuleUtil.isZeroChecked(call); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharRule.java index 5e2575f681..75de3d7dab 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/AppendCharacterWithCharRule.java @@ -41,7 +41,7 @@ public class AppendCharacterWithCharRule extends AbstractJavaRule { JavaNode callParent = node.getParent().getParent(); if (callParent instanceof ASTMethodCall) { ASTMethodCall call = (ASTMethodCall) callParent; - if (call.getMethodName().equals("append") + if ("append".equals(call.getMethodName()) && (TypeTestUtil.isDeclaredInClass(StringBuilder.class, call.getMethodType()) || TypeTestUtil.isDeclaredInClass(StringBuffer.class, call.getMethodType())) ) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java index 9ad8abb92a..84f22d3084 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/InefficientEmptyStringCheckRule.java @@ -58,7 +58,7 @@ public class InefficientEmptyStringCheckRule extends AbstractJavaRulechainRule { } private static boolean isLengthZeroCheck(ASTMethodCall call) { - return call.getMethodName().equals("length") + return "length".equals(call.getMethodName()) && call.getArguments().size() == 0 && JavaRuleUtil.isZeroChecked(call); } @@ -66,7 +66,7 @@ public class InefficientEmptyStringCheckRule extends AbstractJavaRulechainRule { private static boolean isTrimCall(ASTExpression expr) { if (expr instanceof ASTMethodCall) { ASTMethodCall call = (ASTMethodCall) expr; - return call.getMethodName().equals("trim") + return "trim".equals(call.getMethodName()) && call.getArguments().size() == 0 && TypeTestUtil.isA(String.class, call.getQualifier()); } @@ -77,7 +77,7 @@ public class InefficientEmptyStringCheckRule extends AbstractJavaRulechainRule { private static boolean isIsEmptyCall(ASTExpression expr) { if (expr instanceof ASTMethodCall) { ASTMethodCall call = (ASTMethodCall) expr; - return call.getMethodName().equals("isEmpty") + return "isEmpty".equals(call.getMethodName()) && call.getArguments().size() == 0; } return false; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharRule.java index 6a335069c9..d5144314a4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseIndexOfCharRule.java @@ -21,7 +21,7 @@ public class UseIndexOfCharRule extends AbstractJavaRulechainRule { @Override public Object visit(ASTMethodCall node, Object data) { - if (node.getMethodName().equals("indexOf") || node.getMethodName().equals("lastIndexOf")) { + if ("indexOf".equals(node.getMethodName()) || "lastIndexOf".equals(node.getMethodName())) { if (TypeTestUtil.isA(String.class, node.getQualifier()) && node.getArguments().size() >= 1) { // there are two overloads of each ASTExpression arg = node.getArguments().get(0); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java index ed43dae949..d2337e1c5e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java @@ -34,7 +34,7 @@ public class UseStringBufferLengthRule extends AbstractJavaRulechainRule { @Override public Object visit(ASTMethodCall call, Object data) { - if (call.getMethodName().equals("toString") + if ("toString".equals(call.getMethodName()) && call.getArguments().size() == 0 && TypeTestUtil.isA(CharSequence.class, call.getQualifier()) && !TypeTestUtil.isA(String.class, call.getQualifier()) @@ -47,7 +47,7 @@ public class UseStringBufferLengthRule extends AbstractJavaRulechainRule { private boolean isLengthCall(JavaNode node) { if (node instanceof ASTMethodCall) { ASTMethodCall call = (ASTMethodCall) node; - return call.getMethodName().equals("length") + return "length".equals(call.getMethodName()) && call.getArguments().size() == 0; } return false; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java index 117fde1b94..64a2a8c1c7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UselessStringValueOfRule.java @@ -51,7 +51,7 @@ public class UselessStringValueOfRule extends AbstractJavaRule { if (expr instanceof ASTMethodCall) { ASTMethodCall call = (ASTMethodCall) expr; if (call.getArguments().size() == 1 - && call.getMethodName().equals("valueOf") + && "valueOf".equals(call.getMethodName()) && TypeTestUtil.isDeclaredInClass(String.class, call.getMethodType())) { return call.getArguments().get(0); } From 978ebf954b3c10ca46c853e3fe39f245e8811870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 08:29:07 +0100 Subject: [PATCH 096/299] Update UselessOperationOnImmutable --- .travis/all-java.xml | 2 +- .../UselessOperationOnImmutableRule.java | 108 ++++-------------- .../UselessOperationOnImmutableTest.java | 1 - .../xml/UselessOperationOnImmutable.xml | 10 ++ 4 files changed, 31 insertions(+), 90 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 1c2dd06fce..64fe975248 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -268,7 +268,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableRule.java index aae47c5a6e..ee853523e0 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableRule.java @@ -4,108 +4,40 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import static net.sourceforge.pmd.util.CollectionUtil.setOf; +import java.math.BigDecimal; +import java.math.BigInteger; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTLocalVariableDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; -import net.sourceforge.pmd.lang.java.ast.ASTType; -import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** * An operation on an Immutable object (String, BigDecimal or BigInteger) won't * change the object itself. The result of the operation is a new object. * Therefore, ignoring the operation result is an error. */ -public class UselessOperationOnImmutableRule extends AbstractJavaRule { +public class UselessOperationOnImmutableRule extends AbstractJavaRulechainRule { - /** - * These are the BigDecimal methods which are immutable - */ - private static final Set BIG_DECIMAL_METHODS = - setOf(".abs", ".add", ".divide", ".divideToIntegralValue", ".max", ".min", ".movePointLeft", - ".movePointRight", ".multiply", ".negate", ".plus", ".pow", ".remainder", ".round", - ".scaleByPowerOfTen", ".setScale", ".stripTrailingZeros", ".subtract", ".ulp"); - - /** - * These are the BigInteger methods which are immutable - */ - private static final Set BIG_INTEGER_METHODS = - setOf(".abs", ".add", ".and", ".andNot", ".clearBit", ".divide", ".flipBit", ".gcd", ".max", - ".min", ".mod", ".modInverse", ".modPow", ".multiply", ".negate", ".nextProbablePrine", ".not", ".or", - ".pow", ".remainder", ".setBit", ".shiftLeft", ".shiftRight", ".subtract", ".xor"); - - /** - * These are the String methods which are immutable - */ - private static final Set STRING_METHODS = - setOf(".concat", ".intern", ".replace", ".replaceAll", ".replaceFirst", ".substring", - ".toLowerCase", ".toString", ".toUpperCase", ".trim"); - - /** - * These are the classes that the rule can apply to - */ - private static final Map> MAP_CLASSES = new HashMap<>(); - - static { - MAP_CLASSES.put("java.math.BigDecimal", BIG_DECIMAL_METHODS); - MAP_CLASSES.put("BigDecimal", BIG_DECIMAL_METHODS); - MAP_CLASSES.put("java.math.BigInteger", BIG_INTEGER_METHODS); - MAP_CLASSES.put("BigInteger", BIG_INTEGER_METHODS); - MAP_CLASSES.put("java.lang.String", STRING_METHODS); - MAP_CLASSES.put("String", STRING_METHODS); + public UselessOperationOnImmutableRule() { + super(ASTMethodCall.class); } @Override - public Object visit(ASTLocalVariableDeclaration node, Object data) { + public Object visit(ASTMethodCall node, Object data) { + ASTExpression qualifier = node.getQualifier(); + if (node.getParent() instanceof ASTExpressionStatement && qualifier != null) { - ASTVariableDeclaratorId var = getDeclaration(node); - if (var == null) { - return super.visit(node, data); - } - String variableName = var.getImage(); - for (NameOccurrence no : var.getUsages()) { - // FIXME - getUsages will return everything with the same name as - // the variable, - // see JUnit test, case 6. Changing to Node below, revisit when - // getUsages is fixed - Node sn = no.getLocation(); - Node primaryExpression = sn.getParent().getParent(); - Class parentClass = primaryExpression.getParent().getClass(); - if (parentClass.equals(ASTStatementExpression.class)) { - String methodCall = sn.getImage().substring(variableName.length()); - ASTType nodeType = node.getTypeNode(); - if (nodeType != null) { - if (MAP_CLASSES.get(nodeType.getTypeImage()).contains(methodCall)) { - addViolation(data, sn); - } - } - } - } - return super.visit(node, data); - } - - /** - * This method checks the variable declaration if it is on a class we care - * about. If it is, it returns the DeclaratorId - * - * @param node - * The ASTLocalVariableDeclaration which is a problem - * @return ASTVariableDeclaratorId - */ - private ASTVariableDeclaratorId getDeclaration(ASTLocalVariableDeclaration node) { - ASTType type = node.getTypeNode(); - if (type != null) { - if (MAP_CLASSES.keySet().contains(type.getTypeImage())) { - return node.getFirstDescendantOfType(ASTVariableDeclaratorId.class); + // these types are immutable, so any method of those whose + // result is ignored is a violation + if (TypeTestUtil.isA(String.class, qualifier) + || TypeTestUtil.isA(BigDecimal.class, qualifier) + || TypeTestUtil.isA(BigInteger.class, qualifier)) { + addViolation(data, node); } } return null; } + } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableTest.java index 01a50ed0cc..54012b3130 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/UselessOperationOnImmutableTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UselessOperationOnImmutableTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UselessOperationOnImmutable.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UselessOperationOnImmutable.xml index 167fe3d4be..10e48cd971 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UselessOperationOnImmutable.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UselessOperationOnImmutable.xml @@ -10,6 +10,7 @@ useless operation on BigDecimal 1 useless operation on BigInteger 1 using the result, so OK 0 using the result in a method call, so OK 0 BigInteger obtained from compound method call 0 Using generics on List, OK 0 getSolution() { List result = new ArrayList(); @@ -92,6 +98,7 @@ public class Foo { BigInteger in conditional statement 0 1702782, Immutable used in comparison 0 BigInteger calls in expression 0 2645268, ClassCastException using Annotation on Local Field 1 Date: Mon, 2 Nov 2020 09:05:46 +0100 Subject: [PATCH 097/299] Update AvoidEnumAsIdentifier --- .travis/all-java.xml | 2 +- .../lang/java/rule/errorprone/AvoidEnumAsIdentifierTest.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 64fe975248..2bca862d9c 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -182,7 +182,7 @@ - + diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidEnumAsIdentifierTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidEnumAsIdentifierTest.java index aa38545c85..0c936de878 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidEnumAsIdentifierTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidEnumAsIdentifierTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidEnumAsIdentifierTest extends PmdRuleTst { // no additional unit tests } From 769bc5fe5f35a75f24a2b36a68c1dd281a7eb301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 09:08:19 +0100 Subject: [PATCH 098/299] Update AvoidCatchingThrowable --- .travis/all-java.xml | 2 +- .../AvoidCatchingThrowableRule.java | 30 ------------------- .../resources/category/java/errorprone.xml | 9 +++++- .../AvoidCatchingThrowableTest.java | 1 - 4 files changed, 9 insertions(+), 33 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidCatchingThrowableRule.java diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 2bca862d9c..4fd09180ff 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -179,7 +179,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidCatchingThrowableRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidCatchingThrowableRule.java deleted file mode 100644 index 280e1ea57b..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidCatchingThrowableRule.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.errorprone; - -import org.checkerframework.checker.nullness.qual.NonNull; - -import net.sourceforge.pmd.lang.java.ast.ASTCatchClause; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -/** - * Finds catch statements containing throwable as the - * type definition. - * - * @author Trond Andersen - */ -public class AvoidCatchingThrowableRule extends AbstractJavaRule { - - @Override - public Object visit(ASTCatchClause catchStatement, Object data) { - for (@NonNull ASTClassOrInterfaceType caughtException : catchStatement.getParameter().getAllExceptionTypes()) { - if (Throwable.class.equals(caughtException.getType())) { - addViolation(data, catchStatement); - } - } - return super.visit(catchStatement, data); - } -} diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 9e9275a571..b8a2702df1 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -247,13 +247,20 @@ public class Foo { language="java" since="1.2" message="A catch statement should never catch throwable since it includes errors." - class="net.sourceforge.pmd.lang.java.rule.errorprone.AvoidCatchingThrowableRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#avoidcatchingthrowable"> Catching Throwable errors is not recommended since its scope is very broad. It includes runtime issues such as OutOfMemoryError that should be exposed and managed separately. 3 + + + + + Date: Mon, 2 Nov 2020 09:08:52 +0100 Subject: [PATCH 099/299] Update AvoidAssertAsIdentifier --- .travis/all-java.xml | 2 +- .../lang/java/rule/errorprone/AvoidAssertAsIdentifierTest.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 4fd09180ff..e1797f9fc3 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -175,7 +175,7 @@ - + diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidAssertAsIdentifierTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidAssertAsIdentifierTest.java index 3ecd614152..8f1d8d151d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidAssertAsIdentifierTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidAssertAsIdentifierTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidAssertAsIdentifierTest extends PmdRuleTst { // no additional unit tests } From 2be5ab8e752e009e44dce51d1fccc28534cb1c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 09:21:36 +0100 Subject: [PATCH 100/299] Update AvoidDecimalLiteralsInBigDecimalConstructor --- .travis/all-java.xml | 2 +- .../pmd/lang/java/types/TypeTestUtil.java | 6 ++++-- .../main/resources/category/java/errorprone.xml | 8 +++----- ...cimalLiteralsInBigDecimalConstructorTest.java | 1 - .../pmd/lang/java/types/TypeTestUtilTest.java | 16 ++++++++++++++++ 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index e1797f9fc3..aaa1fabc7c 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -180,7 +180,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index efa3cf4967..0c3f488383 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -72,7 +72,7 @@ public final class TypeTestUtil { JTypeMirror otherType = TypesFromReflection.fromReflect(clazz, type.getTypeSystem()); - if (otherType == null || TypeOps.isUnresolved(type)) { + if (otherType == null || TypeOps.isUnresolved(type) || otherType.isPrimitive()) { // We'll return true if the types have equal symbols (same binary name), // but we ignore subtyping. return isExactlyA(clazz, type.getSymbol()); @@ -131,6 +131,8 @@ public final class TypeTestUtil { if (otherType == null || otherType.isClassOrInterface() && ((JClassType) otherType).getSymbol().isAnonymousClass()) { return false; // we know isExactlyA(canonicalName, node); returned false + } else if (otherType.isPrimitive()) { + return otherType == thisType; // isSubtypeOf considers primitive widening like subtyping } return thisType.isSubtypeOf(otherType); @@ -256,7 +258,7 @@ public final class TypeTestUtil { // Neither final nor an annotation. Enums & records have ACC_FINAL // Note: arrays have ACC_FINAL, but have subtypes by covariance // Note: annotations may be implemented by classes - return Modifier.isFinal(clazz.getModifiers()) && !clazz.isArray(); + return Modifier.isFinal(clazz.getModifiers()) && !clazz.isArray() || clazz.isPrimitive(); } } diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index b8a2702df1..81035d9649 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -296,15 +296,13 @@ exactly equal to 0.1, as one would expect. Therefore, it is generally recommend diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDecimalLiteralsInBigDecimalConstructorTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDecimalLiteralsInBigDecimalConstructorTest.java index 94f169f12e..81d14c3042 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDecimalLiteralsInBigDecimalConstructorTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidDecimalLiteralsInBigDecimalConstructorTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class AvoidDecimalLiteralsInBigDecimalConstructorTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java index 7ab99de7b5..5baf537955 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java @@ -127,6 +127,22 @@ public class TypeTestUtilTest extends BaseNonParserTest { assertIsStrictSubtype(arrayT, Object.class); } + @Test + public void testIsAPrimitiveSubtype() { + + ASTType arrayT = + java.parse("import java.io.ObjectStreamField; " + + "class Foo { private static final int serialPersistentFields; }") + .getFirstDescendantOfType(ASTType.class); + + + assertIsExactlyA(arrayT, int.class); + assertIsNot(arrayT, long.class); + assertIsNot(arrayT, double.class); + assertIsNot(arrayT, float.class); + assertIsNot(arrayT, Object.class); + } + @Test public void testIsAFallbackAnnotation() { From 957a35bc82e33ac2a814430dd3440b8d8bee2e2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 09:39:30 +0100 Subject: [PATCH 101/299] Ensure TypeTestUtil throws on parameterized type --- .../pmd/internal/util/AssertionUtil.java | 9 +++ .../pmd/lang/java/types/TypeSystem.java | 4 +- .../pmd/lang/java/types/TypeTestUtil.java | 40 ++++++------- .../pmd/lang/java/types/TypeTestUtilTest.java | 56 ++++++++++--------- 4 files changed, 57 insertions(+), 52 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java index 4d02265a4d..2b06a81c50 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/AssertionUtil.java @@ -23,6 +23,15 @@ public final class AssertionUtil { return PACKAGE_PATTERN.matcher(name).matches(); } + /** + * @throws IllegalArgumentException if the name is not a binary name + */ + public static void assertValidJavaBinaryName(CharSequence name) { + if (!isJavaBinaryName(name)) { + throw new IllegalArgumentException("Not a Java binary name '" + name + "'"); + } + } + public static boolean isJavaBinaryName(CharSequence name) { return name.length() > 0 && PACKAGE_PATTERN.matcher(name).matches(); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeSystem.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeSystem.java index 86f6abe520..7299ac8c6a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeSystem.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeSystem.java @@ -376,9 +376,7 @@ public final class TypeSystem { return getPrimitive(kind).getSymbol(); } - if (!AssertionUtil.isJavaBinaryName(name)) { - throw new IllegalArgumentException("Not a binary name '" + name + "'"); - } + AssertionUtil.assertValidJavaBinaryName(name); return isCanonical ? resolver.resolveClassFromCanonicalName(name) : resolver.resolveClassFromBinaryName(name); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index 0c3f488383..1883c03d2c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -46,6 +46,9 @@ public final class TypeTestUtil { *

If either type is unresolved, the types are tested for equality, * thus giving more useful results than {@link JTypeMirror#isSubtypeOf(JTypeMirror)}. * + *

Note that primitives are NOT considered subtypes of one another + * by this method, even though {@link JTypeMirror#isSubtypeOf(JTypeMirror)} does. + * * @param clazz a class (non-null) * @param node the type node to check * @@ -84,24 +87,18 @@ public final class TypeTestUtil { /** * Checks whether the static type of the node is a subtype of the - * class identified by the given name. This ignores type arguments, - * if the type of the node is parameterized. Examples: - * - *

{@code
-     * isA("java.util.List", ()>)      = true
-     * isA("java.util.ArrayList", ()>) = true
-     * isA("int[]", )                            = true
-     * isA("java.lang.Object[]", )            = true
-     * isA(_, null) = false
-     * isA(null, _) = NullPointerException
-     * }
+ * class identified by the given name. See {@link #isA(Class, TypeNode)} + * for examples and more info. * * @param canonicalName the canonical name of a class or array type (without whitespace) * @param node the type node to check * * @return true if the type test matches * - * @throws NullPointerException if the class name parameter is null + * @throws NullPointerException if the class name parameter is null + * @throws IllegalArgumentException if the class name parameter is not a valid java binary name, + * eg it has type arguments + * @see #isA(Class, TypeNode) */ public static boolean isA(final @NonNull String canonicalName, final @Nullable TypeNode node) { AssertionUtil.requireParamNotNull("canonicalName", (Object) canonicalName); @@ -139,6 +136,7 @@ public final class TypeTestUtil { } private static boolean isAnnotationSuperType(String clazzName) { + AssertionUtil.assertValidJavaBinaryName(clazzName); // then, the supertype may only be Object, j.l.Annotation // this is used e.g. by the typeIs function in XPath return "java.lang.annotation.Annotation".equals(clazzName) @@ -209,24 +207,18 @@ public final class TypeTestUtil { /** * Checks whether the static type of the node is exactly the type - * given by the name. This ignores strict supertypes, and type arguments - * if the type of the node is parameterized. - * - *
{@code
-     * isExactlyA(List.class, ()>)      = false
-     * isExactlyA(ArrayList.class, ()>) = true
-     * isExactlyA(int[].class, )                  = true
-     * isExactlyA(Object[].class, )            = false
-     * isExactlyA(_, null) = false
-     * isExactlyA(null, _) = NullPointerException
-     * }
+ * given by the name. See {@link #isExactlyA(Class, TypeNode)} for + * examples and more info. * * @param canonicalName a canonical name of a class or array type * @param node the type node to check * * @return true if the node is non-null and has the given type * - * @throws NullPointerException if the class name parameter is null + * @throws NullPointerException if the class name parameter is null + * @throws IllegalArgumentException if the class name parameter is not a valid java binary name, + * eg it has type arguments + * @see #isExactlyA(Class, TypeNode) */ public static boolean isExactlyA(@NonNull String canonicalName, final @Nullable TypeNode node) { return isExactlyAOrAnon(canonicalName, node) == OptionalBool.YES; diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java index 5baf537955..2030d30b3d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java @@ -12,12 +12,12 @@ import java.util.concurrent.Callable; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; -import org.junit.function.ThrowingRunnable; import org.junit.rules.ExpectedException; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnonymousClassDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTType; @@ -159,6 +159,32 @@ public class TypeTestUtilTest extends BaseNonParserTest { assertIsA(klass, Object.class); } + @Test + public void testIsAStringWithTypeArguments() { + + ASTAnyTypeDeclaration klass = + java.parse("package org;" + + "public class FooBar {}") + .getFirstDescendantOfType(ASTAnyTypeDeclaration.class); + + + expect.expect(IllegalArgumentException.class); + TypeTestUtil.isA("java.util.List", klass); + } + + @Test + public void testIsAStringWithTypeArgumentsAnnotation() { + + ASTAnyTypeDeclaration klass = + java.parse("package org;" + + "public @interface FooBar {}") + .getFirstDescendantOfType(ASTAnyTypeDeclaration.class); + + + expect.expect(IllegalArgumentException.class); + TypeTestUtil.isA("java.util.List", klass); + } + @Test public void testAnonClassTypeNPE() { // #2756 @@ -214,30 +240,10 @@ public class TypeTestUtilTest extends BaseNonParserTest { .getFirstDescendantOfType(ASTAnnotation.class); Assert.assertNotNull(node); - Assert.assertThrows(NullPointerException.class, new ThrowingRunnable() { - @Override - public void run() { - TypeTestUtil.isA((String) null, node); - } - }); - Assert.assertThrows(NullPointerException.class, new ThrowingRunnable() { - @Override - public void run() { - TypeTestUtil.isA((Class) null, node); - } - }); - Assert.assertThrows(NullPointerException.class, new ThrowingRunnable() { - @Override - public void run() { - TypeTestUtil.isExactlyA((Class) null, node); - } - }); - Assert.assertThrows(NullPointerException.class, new ThrowingRunnable() { - @Override - public void run() { - TypeTestUtil.isExactlyA((String) null, node); - } - }); + Assert.assertThrows(NullPointerException.class, () -> TypeTestUtil.isA((String) null, node)); + Assert.assertThrows(NullPointerException.class, () -> TypeTestUtil.isA((Class) null, node)); + Assert.assertThrows(NullPointerException.class, () -> TypeTestUtil.isExactlyA((Class) null, node)); + Assert.assertThrows(NullPointerException.class, () -> TypeTestUtil.isExactlyA((String) null, node)); } private void assertIsA(TypeNode node, Class type) { From 3e69ba11300afeee436115d4c65deb8cf2869ae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 09:49:56 +0100 Subject: [PATCH 102/299] Update WhileLoopWithLiteralBoolean --- .../src/main/resources/category/java/bestpractices.xml | 8 ++++---- .../bestpractices/WhileLoopWithLiteralBooleanTest.java | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 0af60234db..45cf5f0d77 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -1907,10 +1907,10 @@ a block `{}` is sufficient. 3 - -//DoStatement[Expression/PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral] | -//WhileStatement[Expression/PrimaryExpression/PrimaryPrefix/Literal/BooleanLiteral[@True = false()]] - + diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/WhileLoopWithLiteralBooleanTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/WhileLoopWithLiteralBooleanTest.java index ffbbe9192e..31fcad9210 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/WhileLoopWithLiteralBooleanTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/WhileLoopWithLiteralBooleanTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class WhileLoopWithLiteralBooleanTest extends PmdRuleTst { // no additional unit tests } From 89a1ec7b5547a67fd8ead975df2a610694f444d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 10:04:57 +0100 Subject: [PATCH 103/299] Update AvoidMultipleUnaryOperators --- .travis/all-java.xml | 2 +- .../AvoidMultipleUnaryOperatorsRule.java | 72 ------------------- .../resources/category/java/errorprone.xml | 10 ++- .../AvoidMultipleUnaryOperatorsTest.java | 1 - .../xml/AvoidMultipleUnaryOperators.xml | 9 ++- 5 files changed, 16 insertions(+), 78 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidMultipleUnaryOperatorsRule.java diff --git a/.travis/all-java.xml b/.travis/all-java.xml index aaa1fabc7c..212e305686 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -188,7 +188,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidMultipleUnaryOperatorsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidMultipleUnaryOperatorsRule.java deleted file mode 100644 index be73e9f279..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/AvoidMultipleUnaryOperatorsRule.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.errorprone; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTUnaryExpressionNotPlusMinus; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; - -public class AvoidMultipleUnaryOperatorsRule extends AbstractJavaRule { - - public AvoidMultipleUnaryOperatorsRule() { - super.addRuleChainVisit(ASTUnaryExpression.class); - super.addRuleChainVisit(ASTUnaryExpressionNotPlusMinus.class); - } - - @Override - public Object visit(ASTUnaryExpression node, Object data) { - checkUnaryDescendent(node, data); - return data; - } - - @Override - public Object visit(ASTUnaryExpressionNotPlusMinus node, Object data) { - checkUnaryDescendent(node, data); - return data; - } - - private void checkUnaryDescendent(Node node, Object data) { - boolean match = false; - if (node.getNumChildren() == 1) { - Node child = node.getChild(0); - if (child instanceof ASTUnaryExpression || child instanceof ASTUnaryExpressionNotPlusMinus) { - match = true; - } else if (child instanceof ASTPrimaryExpression) { - Node primaryExpression = child; - // Skip down PrimaryExpression/PrimaryPrefix/Expression chains - // created by parentheses - while (true) { - if (primaryExpression.getNumChildren() == 1 - && primaryExpression.getChild(0) instanceof ASTPrimaryPrefix - && primaryExpression.getChild(0).getNumChildren() == 1 - && primaryExpression.getChild(0).getChild(0) instanceof ASTExpression - && primaryExpression.getChild(0).getChild(0).getNumChildren() == 1) { - Node candidate = primaryExpression.getChild(0).getChild(0).getChild(0); - if (candidate instanceof ASTUnaryExpression - || candidate instanceof ASTUnaryExpressionNotPlusMinus) { - match = true; - break; - } else if (candidate instanceof ASTPrimaryExpression) { - primaryExpression = candidate; - continue; - } else { - break; - } - } else { - break; - } - } - } - } - - if (match) { - addViolation(data, node); - } - } -} diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 81035d9649..c8c1ae08f0 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -581,7 +581,7 @@ public void bar() { @@ -589,6 +589,14 @@ The use of multiple unary operators may be problematic, and/or confusing. Ensure that the intended usage is not a bug, or consider simplifying the expression. 2 + + + + + Compound - 8 + 4 + 2,3,4,5 Compound with parentheses - 8 + 4 + 2,3,4,5 Compound with parentheses an mixed operators - 9 + 3 + 2,3,4 Date: Mon, 2 Nov 2020 10:07:51 +0100 Subject: [PATCH 104/299] Fix report locations for var ids --- .../pmd/lang/java/ast/ASTLocalVariableDeclaration.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclaration.java index 39274f18c7..6ffdc81285 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTLocalVariableDeclaration.java @@ -4,6 +4,10 @@ package net.sourceforge.pmd.lang.java.ast; +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; + /** * Represents a local variable declaration. This is a {@linkplain ASTStatement statement}, * but the node is also used in {@linkplain ASTForInit for-loop initialisers} and @@ -31,6 +35,10 @@ public final class ASTLocalVariableDeclaration extends AbstractJavaNode super(id); } + @Override + protected @Nullable JavaccToken getPreferredReportLocation() { + return getVarIds().firstOrThrow().getFirstToken(); + } @Override protected R acceptVisitor(JavaVisitor visitor, P data) { From e3b2d2d0e67520ed0f2d53d0623429345cbfe216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 10:44:43 +0100 Subject: [PATCH 105/299] Fix some errors --- pmd-java/src/main/resources/category/java/bestpractices.xml | 2 +- pmd-java/src/main/resources/category/java/multithreading.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 45cf5f0d77..bcadf01722 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -187,7 +187,7 @@ Avoid printStackTrace(); use a logger call instead. //MethodCall[ @MethodName = 'printStackTrace' and ArgumentList[@Size = 0] - and child::*[1][pmd-java:typeIs('java.lang.Throwable')] + and child::*[1][pmd-java:nodeIs('Expression') and pmd-java:typeIs('java.lang.Throwable')] ] ]]> diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index d97c58cfea..04d51c9174 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -152,7 +152,7 @@ Also EJB's might be moved between machines in a cluster and only managed resourc @@ -207,7 +207,7 @@ Explicitly calling Thread.run() method will execute in the caller's thread of co From e428ed7255c4daf6e9e057804975b8bbbb97deb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 11:26:51 +0100 Subject: [PATCH 106/299] Add signature matcher xpath function --- .../java/internal/JavaLanguageHandler.java | 2 + .../internal/MatchesSignatureFunction.java | 92 +++++++++ .../pmd/lang/java/types/TypeTestUtil.java | 180 +++++++++++++++++- .../resources/category/java/bestpractices.xml | 6 +- .../category/java/multithreading.xml | 2 +- .../resources/category/java/performance.xml | 5 +- 6 files changed, 270 insertions(+), 17 deletions(-) create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java index b5fd9ec0b5..c3a41d8329 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java @@ -21,6 +21,7 @@ import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleViolationFactory; import net.sourceforge.pmd.lang.java.rule.xpath.internal.BaseContextNodeTestFun; import net.sourceforge.pmd.lang.java.rule.xpath.internal.GetCommentOnFunction; import net.sourceforge.pmd.lang.java.rule.xpath.internal.GetModifiersFun; +import net.sourceforge.pmd.lang.java.rule.xpath.internal.MatchesSignatureFunction; import net.sourceforge.pmd.lang.java.rule.xpath.internal.MetricFunction; import net.sourceforge.pmd.lang.java.rule.xpath.internal.NodeIsFunction; import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; @@ -37,6 +38,7 @@ public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler { BaseContextNodeTestFun.TYPE_IS_EXACTLY, BaseContextNodeTestFun.TYPE_IS, BaseContextNodeTestFun.HAS_ANNOTATION, + MatchesSignatureFunction.INSTANCE, NodeIsFunction.INSTANCE, GetModifiersFun.GET_EFFECTIVE, GetModifiersFun.GET_EXPLICIT, diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java new file mode 100644 index 0000000000..173e6c2784 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java @@ -0,0 +1,92 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.xpath.internal; + +import static net.sourceforge.pmd.lang.java.rule.xpath.internal.BaseContextNodeTestFun.SINGLE_STRING_SEQ; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.InvocationNode; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil.MethodSigMatcher; +import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode; + +import net.sf.saxon.expr.Expression; +import net.sf.saxon.expr.StaticContext; +import net.sf.saxon.expr.StringLiteral; +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.lib.ExtensionFunctionCall; +import net.sf.saxon.om.Sequence; +import net.sf.saxon.trans.XPathException; +import net.sf.saxon.value.BooleanValue; +import net.sf.saxon.value.SequenceType; + +public final class MatchesSignatureFunction extends BaseJavaXPathFunction { + + public static final MatchesSignatureFunction INSTANCE = new MatchesSignatureFunction(); + + private MatchesSignatureFunction() { + super("matchesSig"); + } + + @Override + public SequenceType[] getArgumentTypes() { + return SINGLE_STRING_SEQ; + } + + @Override + public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { + return SequenceType.SINGLE_BOOLEAN; + } + + @Override + public boolean dependsOnFocus() { + return true; + } + + @Override + public ExtensionFunctionCall makeCallExpression() { + return new ExtensionFunctionCall() { + + private MethodSigMatcher constantTestType = null; + + @Override + public Expression rewrite(StaticContext context, Expression[] arguments) throws XPathException { + // If the argument is a string literal then we can preload + // the class, and check that it's valid at expression build time + + Expression firstArg = arguments[0]; // this expression has been type checked so there is an argument + if (firstArg instanceof StringLiteral) { + String name = ((StringLiteral) firstArg).getStringValue(); + constantTestType = getClassFromArg(name); + } + return null; + } + + @Override + public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { + Node node = ((AstElementNode) context.getContextItem()).getUnderlyingNode(); + if (!(node instanceof InvocationNode)) { + throw new XPathException("Not an InvocationNode: " + node.getXPathNodeName()); + } + MethodSigMatcher matcher = constantTestType != null ? constantTestType + : getClassFromArg(arguments[0].head().getStringValue()); + return BooleanValue.get(matcher.matchesCall((InvocationNode) node)); + } + }; + } + + private static MethodSigMatcher getClassFromArg(String name) throws XPathException { + String[] split = name.split("#"); + if (split.length != 2) { + throw new XPathException("Invalid signature " + name); + } + + try { + return MethodSigMatcher.parse(split[0], split[1]); + } catch (IllegalArgumentException e) { + throw new XPathException(e); + } + } + +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index 1883c03d2c..b3bff3374c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -5,13 +5,18 @@ package net.sourceforge.pmd.lang.java.types; import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import org.apache.commons.lang3.StringUtils; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.internal.util.AssertionUtil; +import net.sourceforge.pmd.lang.java.ast.ASTList; import net.sourceforge.pmd.lang.java.ast.InternalApiBridge; +import net.sourceforge.pmd.lang.java.ast.InvocationNode; import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; @@ -105,12 +110,27 @@ public final class TypeTestUtil { if (node == null) { return false; } - OptionalBool exactMatch = isExactlyAOrAnon(canonicalName, node); + + UnresolvedClassStore unresolvedStore = InternalApiBridge.getProcessor(node).getUnresolvedStore(); + return isA(canonicalName, node.getTypeMirror(), unresolvedStore); + } + + private static boolean isA(@NonNull String canonicalName, @Nullable JTypeMirror thisType) { + AssertionUtil.requireParamNotNull("canonicalName", (Object) canonicalName); + if (thisType == null) { + return false; + } + + return isA(canonicalName, thisType, null); + } + + private static boolean isA(@NonNull String canonicalName, @NonNull JTypeMirror thisType, @Nullable UnresolvedClassStore unresolvedStore) { + + OptionalBool exactMatch = isExactlyAOrAnon(canonicalName, thisType); if (exactMatch != OptionalBool.NO) { return exactMatch == OptionalBool.YES; // otherwise anon, and we return false } - JTypeMirror thisType = node.getTypeMirror(); JTypeDeclSymbol thisClass = thisType.getSymbol(); if (thisClass instanceof JClassSymbol && ((JClassSymbol) thisClass).isAnnotation()) { return isAnnotationSuperType(canonicalName); @@ -123,7 +143,6 @@ public final class TypeTestUtil { } TypeSystem ts = thisType.getTypeSystem(); - UnresolvedClassStore unresolvedStore = InternalApiBridge.getProcessor(node).getUnresolvedStore(); @Nullable JTypeMirror otherType = TypesFromReflection.loadType(ts, canonicalName, unresolvedStore); if (otherType == null || otherType.isClassOrInterface() && ((JClassType) otherType).getSymbol().isAnonymousClass()) { @@ -221,16 +240,16 @@ public final class TypeTestUtil { * @see #isExactlyA(Class, TypeNode) */ public static boolean isExactlyA(@NonNull String canonicalName, final @Nullable TypeNode node) { - return isExactlyAOrAnon(canonicalName, node) == OptionalBool.YES; + if (node == null) { + return false; + } + return isExactlyAOrAnon(canonicalName, node.getTypeMirror()) == OptionalBool.YES; } - private static OptionalBool isExactlyAOrAnon(@NonNull String canonicalName, final @Nullable TypeNode node) { + private static OptionalBool isExactlyAOrAnon(@NonNull String canonicalName, final @NonNull JTypeMirror node) { AssertionUtil.requireParamNotNull("canonicalName", canonicalName); - if (node == null) { - return OptionalBool.NO; - } - JTypeDeclSymbol sym = node.getTypeMirror().getSymbol(); + JTypeDeclSymbol sym = node.getSymbol(); if (sym == null || sym instanceof JTypeParameterSymbol) { return OptionalBool.NO; } @@ -253,4 +272,147 @@ public final class TypeTestUtil { return Modifier.isFinal(clazz.getModifiers()) && !clazz.isArray() || clazz.isPrimitive(); } + + /** + * Matches a method signature. + */ + public static final class MethodSigMatcher { + + final String expectedName; + final List argMatchers; + final TypeMatcher qualifierMatcher; + + MethodSigMatcher(TypeMatcher qualifierMatcher, String expectedName, List argMatchers) { + this.expectedName = expectedName; + this.argMatchers = argMatchers; + this.qualifierMatcher = qualifierMatcher; + } + + public boolean matchesCall(InvocationNode node) { + if (!node.getMethodName().equals(expectedName) + || ASTList.sizeOrZero(node.getArguments()) != argMatchers.size()) { + return false; + } + OverloadSelectionResult info = node.getOverloadSelectionInfo(); + if (info.isFailed()) { + return false; + } + return matchesSig(info.getMethodType()); + } + + public boolean matchesSig(JMethodSig invoc) { + if (!invoc.getName().equals(expectedName)) { + return false; + } + List formals = invoc.getFormalParameters(); + if (invoc.getArity() != argMatchers.size()) { + return false; + } + for (int i = 0; i < formals.size(); i++) { + if (!argMatchers.get(i).matches(formals.get(i))) { + return false; + } + } + return qualifierMatcher.matches(invoc.getDeclaringType()); + } + + + /** + * Parses a {@link MethodSigMatcher} from a string. + * + * @param qualifierName Type matcher for the qualifier (either "_", or a qualified name). + * This will be matched with {@link #isA(String, TypeNode)} + * @param sig A signature in the form {@code name(arg1, arg2, ...)}, + * where each {@code argi} is either {@code _} or a qualified + * type name, without type arguments. + * These will be matched with {@link #isExactlyA(String, TypeNode)}. + * + * @return A sig matcher + * + * @throws IllegalArgumentException If the parameters are malformed + * @throws NullPointerException If the parameters are null + */ + public static MethodSigMatcher parse(String qualifierName, String sig) { + AssertionUtil.assertValidJavaBinaryName(qualifierName); + int i = 0; + while (i < sig.length() && Character.isJavaIdentifierPart(sig.charAt(i))) { + i++; + } + final String methodName = sig.substring(0, i); + if (methodName.isEmpty()) { + throw new IllegalArgumentException("Not a valid signature " + sig); + } + i = consumeChar(sig, i, '('); + if (isChar(sig, i, ')')) { + return new MethodSigMatcher(newMatcher(qualifierName, false), methodName, Collections.emptyList()); + } + List argMatchers = new ArrayList<>(); + i = parseArgList(sig, i, argMatchers); + if (i != sig.length()) { + throw new IllegalArgumentException("Not a valid signature " + sig); + } + return new MethodSigMatcher(newMatcher(qualifierName, false), methodName, argMatchers); + } + + private static int parseArgList(String sig, int i, List argMatchers) { + while (i < sig.length()) { + i = parseType(sig, i, argMatchers, true); + if (isChar(sig, i, ')')) { + return i + 1; + } + i = consumeChar(sig, i, ','); + } + throw new IllegalArgumentException("Not a valid signature " + sig); + } + + private static int consumeChar(String source, int i, char c) { + if (isChar(source, i, c)) { + return i + 1; + } + throw new IllegalArgumentException("Expected " + c + " at index " + i); + } + + private static boolean isChar(String source, int i, char c) { + return i < source.length() && source.charAt(i) == c; + } + + private static int parseType(String source, int i, List result, boolean exact) { + final int start = i; + while (i < source.length() && (Character.isJavaIdentifierPart(source.charAt(i)) + || source.charAt(i) == '.')) { + i++; + } + String name = source.substring(start, i); + AssertionUtil.assertValidJavaBinaryName(name); + result.add(newMatcher(name, exact)); + return i; + } + + private static TypeMatcher newMatcher(String name, boolean exact) { + return "_".equals(name) ? TypeMatcher.ANY : new TypeMatcher(name, exact); + } + + private static final class TypeMatcher { + + /** Matches any type. */ + public static final TypeMatcher ANY = new TypeMatcher(null, false); + + final @Nullable String name; + private final boolean exact; + + private TypeMatcher(@Nullable String name, boolean exact) { + this.name = name; + this.exact = exact; + } + + boolean matches(JTypeMirror type) { + if (name == null) { + return true; + } + return exact ? TypeTestUtil.isExactlyAOrAnon(name, type) == OptionalBool.YES + : TypeTestUtil.isA(name, type); + } + } + } + } diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index bcadf01722..089826f288 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -184,11 +184,7 @@ Avoid printStackTrace(); use a logger call instead. diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index 04d51c9174..b45eb8db91 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -207,7 +207,7 @@ Explicitly calling Thread.run() method will execute in the caller's thread of co diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 973a604ed1..63daba98bd 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -683,8 +683,9 @@ ensure that the string is not empty by making an additional check first. From 16692fafda0842b3f843a9a27e1657bab4c7b0d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 12:02:25 +0100 Subject: [PATCH 107/299] Abstract some logic --- .../xpath/internal/BaseRewrittenFunction.java | 95 +++++++++++++++++++ .../internal/MatchesSignatureFunction.java | 71 ++------------ .../rule/xpath/internal/NodeIsFunction.java | 73 +++----------- .../pmd/lang/java/types/TypeTestUtil.java | 71 +++++++++----- 4 files changed, 160 insertions(+), 150 deletions(-) create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java new file mode 100644 index 0000000000..58d1a1e801 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java @@ -0,0 +1,95 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.xpath.internal; + +import static net.sourceforge.pmd.lang.java.rule.xpath.internal.BaseContextNodeTestFun.SINGLE_STRING_SEQ; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode; + +import net.sf.saxon.expr.Expression; +import net.sf.saxon.expr.StaticContext; +import net.sf.saxon.expr.StringLiteral; +import net.sf.saxon.expr.XPathContext; +import net.sf.saxon.lib.ExtensionFunctionCall; +import net.sf.saxon.om.Sequence; +import net.sf.saxon.trans.XPathException; +import net.sf.saxon.value.BooleanValue; +import net.sf.saxon.value.SequenceType; + + +/** + * A context node test function that may parse its string argument early + * if it is a string literal. + */ +abstract class BaseRewrittenFunction extends BaseJavaXPathFunction { + + private final Class contextNodeType; + + protected BaseRewrittenFunction(String localName, Class contextNodeType) { + super(localName); + this.contextNodeType = contextNodeType; + } + + @Override + public SequenceType[] getArgumentTypes() { + return SINGLE_STRING_SEQ; + } + + @Override + public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { + return SequenceType.SINGLE_BOOLEAN; + } + + @Override + public boolean dependsOnFocus() { + return true; + } + + + protected abstract S parseArgument(String constantArg) throws XPathException; + + protected abstract boolean matches(N contextNode, String arg, S parsedArg, boolean isConstant) throws XPathException; + + + @Override + public ExtensionFunctionCall makeCallExpression() { + return new ExtensionFunctionCall() { + + private S constantState; + private boolean isConstant; + + @Override + public Expression rewrite(StaticContext context, Expression[] arguments) throws XPathException { + // If the argument is a string literal then we can preload + // the class, and check that it's valid at expression build time + + Expression firstArg = arguments[0]; // this expression has been type checked so there is an argument + if (firstArg instanceof StringLiteral) { + String name = ((StringLiteral) firstArg).getStringValue(); + constantState = parseArgument(name); + isConstant = true; + } + return null; + } + + @Override + public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { + Node node = ((AstElementNode) context.getContextItem()).getUnderlyingNode(); + if (!contextNodeType.isInstance(node)) { + throw new XPathException( + "Invalid node type: " + node.getXPathNodeName() + ", expected " + contextNodeType); + } + + String arg = arguments[0].head().getStringValue(); + S parsedArg = isConstant ? constantState + : parseArgument(arg); + + return BooleanValue.get(matches((N) node, arg, parsedArg, isConstant)); + } + }; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java index 173e6c2784..baa50d7551 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java @@ -4,86 +4,33 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; -import static net.sourceforge.pmd.lang.java.rule.xpath.internal.BaseContextNodeTestFun.SINGLE_STRING_SEQ; - -import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.InvocationNode; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil.MethodSigMatcher; -import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil.InvocationMatcher; -import net.sf.saxon.expr.Expression; -import net.sf.saxon.expr.StaticContext; -import net.sf.saxon.expr.StringLiteral; -import net.sf.saxon.expr.XPathContext; -import net.sf.saxon.lib.ExtensionFunctionCall; -import net.sf.saxon.om.Sequence; import net.sf.saxon.trans.XPathException; -import net.sf.saxon.value.BooleanValue; -import net.sf.saxon.value.SequenceType; -public final class MatchesSignatureFunction extends BaseJavaXPathFunction { +public final class MatchesSignatureFunction extends BaseRewrittenFunction { public static final MatchesSignatureFunction INSTANCE = new MatchesSignatureFunction(); private MatchesSignatureFunction() { - super("matchesSig"); + super("matchesSig", InvocationNode.class); } @Override - public SequenceType[] getArgumentTypes() { - return SINGLE_STRING_SEQ; + protected boolean matches(InvocationNode contextNode, String arg, InvocationMatcher parsedArg, boolean isConstant) throws XPathException { + return parsedArg.matchesCall(contextNode); } @Override - public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { - return SequenceType.SINGLE_BOOLEAN; - } - - @Override - public boolean dependsOnFocus() { - return true; - } - - @Override - public ExtensionFunctionCall makeCallExpression() { - return new ExtensionFunctionCall() { - - private MethodSigMatcher constantTestType = null; - - @Override - public Expression rewrite(StaticContext context, Expression[] arguments) throws XPathException { - // If the argument is a string literal then we can preload - // the class, and check that it's valid at expression build time - - Expression firstArg = arguments[0]; // this expression has been type checked so there is an argument - if (firstArg instanceof StringLiteral) { - String name = ((StringLiteral) firstArg).getStringValue(); - constantTestType = getClassFromArg(name); - } - return null; - } - - @Override - public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { - Node node = ((AstElementNode) context.getContextItem()).getUnderlyingNode(); - if (!(node instanceof InvocationNode)) { - throw new XPathException("Not an InvocationNode: " + node.getXPathNodeName()); - } - MethodSigMatcher matcher = constantTestType != null ? constantTestType - : getClassFromArg(arguments[0].head().getStringValue()); - return BooleanValue.get(matcher.matchesCall((InvocationNode) node)); - } - }; - } - - private static MethodSigMatcher getClassFromArg(String name) throws XPathException { - String[] split = name.split("#"); + protected InvocationMatcher parseArgument(String constantArg) throws XPathException { + String[] split = constantArg.split("#"); if (split.length != 2) { - throw new XPathException("Invalid signature " + name); + throw new XPathException("Invalid signature " + constantArg); } try { - return MethodSigMatcher.parse(split[0], split[1]); + return InvocationMatcher.parse(split[0], split[1]); } catch (IllegalArgumentException e) { throw new XPathException(e); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunction.java index c6d9ac74b0..436830c00b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunction.java @@ -4,81 +4,30 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; -import static net.sourceforge.pmd.lang.java.rule.xpath.internal.BaseContextNodeTestFun.SINGLE_STRING_SEQ; +import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode; - -import net.sf.saxon.expr.Expression; -import net.sf.saxon.expr.StaticContext; -import net.sf.saxon.expr.StringLiteral; -import net.sf.saxon.expr.XPathContext; -import net.sf.saxon.lib.ExtensionFunctionCall; -import net.sf.saxon.om.Sequence; import net.sf.saxon.trans.XPathException; -import net.sf.saxon.value.BooleanValue; -import net.sf.saxon.value.SequenceType; -public final class NodeIsFunction extends BaseJavaXPathFunction { +public final class NodeIsFunction extends BaseRewrittenFunction, JavaNode> { public static final NodeIsFunction INSTANCE = new NodeIsFunction(); private NodeIsFunction() { - super("nodeIs"); + super("nodeIs", JavaNode.class); } @Override - public SequenceType[] getArgumentTypes() { - return SINGLE_STRING_SEQ; - } - - @Override - public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { - return SequenceType.SINGLE_BOOLEAN; - } - - @Override - public boolean dependsOnFocus() { - return true; - } - - @Override - public ExtensionFunctionCall makeCallExpression() { - return new ExtensionFunctionCall() { - - private Class constantTestType = null; - - @Override - public Expression rewrite(StaticContext context, Expression[] arguments) throws XPathException { - // If the argument is a string literal then we can preload - // the class, and check that it's valid at expression build time - - Expression firstArg = arguments[0]; // this expression has been type checked so there is an argument - if (firstArg instanceof StringLiteral) { - String name = ((StringLiteral) firstArg).getStringValue(); - constantTestType = getClassFromArg(name); - } - return null; - } - - @Override - public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { - Node node = ((AstElementNode) context.getContextItem()).getUnderlyingNode(); - Class klass = constantTestType != null ? constantTestType - : getClassFromArg(arguments[0].head().getStringValue()); - return BooleanValue.get(klass.isInstance(node)); - } - }; - } - - private static Class getClassFromArg(String name) throws XPathException { - Class klass; + protected Class parseArgument(String constantArg) throws XPathException { try { - klass = Class.forName("net.sourceforge.pmd.lang.java.ast.AST" + name); + return Class.forName("net.sourceforge.pmd.lang.java.ast.AST" + constantArg); } catch (ClassNotFoundException e) { - throw new XPathException("No class named AST" + name); + throw new XPathException("No class named AST" + constantArg); } - return klass; + } + + @Override + protected boolean matches(JavaNode contextNode, String arg, Class parsedArg, boolean isConstant) throws XPathException { + return parsedArg.isInstance(contextNode); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index b3bff3374c..83edc6ef20 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -14,9 +14,11 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.internal.util.AssertionUtil; +import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTList; import net.sourceforge.pmd.lang.java.ast.InternalApiBridge; import net.sourceforge.pmd.lang.java.ast.InvocationNode; +import net.sourceforge.pmd.lang.java.ast.QualifiableExpression; import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; @@ -274,53 +276,72 @@ public final class TypeTestUtil { /** - * Matches a method signature. + * Matches a method or constructor call against a particular overload. */ - public static final class MethodSigMatcher { + public static final class InvocationMatcher { final String expectedName; final List argMatchers; final TypeMatcher qualifierMatcher; - MethodSigMatcher(TypeMatcher qualifierMatcher, String expectedName, List argMatchers) { + InvocationMatcher(TypeMatcher qualifierMatcher, String expectedName, List argMatchers) { this.expectedName = expectedName; this.argMatchers = argMatchers; this.qualifierMatcher = qualifierMatcher; } + /** + * Returns true if the call matches this matcher. This means, + * the called overload is the one identified by the argument + * matchers, and the actual qualifier type is a subtype of the + * one mentioned by the qualifier matcher. + */ public boolean matchesCall(InvocationNode node) { if (!node.getMethodName().equals(expectedName) || ASTList.sizeOrZero(node.getArguments()) != argMatchers.size()) { return false; } OverloadSelectionResult info = node.getOverloadSelectionInfo(); - if (info.isFailed()) { + if (info.isFailed() || !matchQualifier(node)) { return false; } - return matchesSig(info.getMethodType()); + return argsMatchOverload(info.getMethodType()); } - public boolean matchesSig(JMethodSig invoc) { - if (!invoc.getName().equals(expectedName)) { - return false; + private boolean matchQualifier(InvocationNode node) { + if (qualifierMatcher == TypeMatcher.ANY) { + return true; } + if (node instanceof QualifiableExpression) { + ASTExpression qualifier = ((QualifiableExpression) node).getQualifier(); + if (qualifier != null) { + return qualifierMatcher.matches(qualifier.getTypeMirror(), false); + } + // todo: if qualifier == null, then we should take the type of the + // implicit receiver, ie `this` or `SomeOuter.this` + } + + return qualifierMatcher.matches(node.getMethodType().getDeclaringType(), false); + } + + private boolean argsMatchOverload(JMethodSig invoc) { List formals = invoc.getFormalParameters(); if (invoc.getArity() != argMatchers.size()) { return false; } for (int i = 0; i < formals.size(); i++) { - if (!argMatchers.get(i).matches(formals.get(i))) { + if (!argMatchers.get(i).matches(formals.get(i), true)) { return false; } } - return qualifierMatcher.matches(invoc.getDeclaringType()); + return true; } /** - * Parses a {@link MethodSigMatcher} from a string. + * Parses a {@link InvocationMatcher} from a string. * - * @param qualifierName Type matcher for the qualifier (either "_", or a qualified name). + * @param qualifierMatcher Type matcher for the qualifier (either "_", or a qualified name). * This will be matched with {@link #isA(String, TypeNode)} * @param sig A signature in the form {@code name(arg1, arg2, ...)}, * where each {@code argi} is either {@code _} or a qualified @@ -332,8 +353,8 @@ public final class TypeTestUtil { * @throws IllegalArgumentException If the parameters are malformed * @throws NullPointerException If the parameters are null */ - public static MethodSigMatcher parse(String qualifierName, String sig) { - AssertionUtil.assertValidJavaBinaryName(qualifierName); + public static InvocationMatcher parse(String qualifierMatcher, String sig) { + AssertionUtil.assertValidJavaBinaryName(qualifierMatcher); int i = 0; while (i < sig.length() && Character.isJavaIdentifierPart(sig.charAt(i))) { i++; @@ -344,19 +365,19 @@ public final class TypeTestUtil { } i = consumeChar(sig, i, '('); if (isChar(sig, i, ')')) { - return new MethodSigMatcher(newMatcher(qualifierName, false), methodName, Collections.emptyList()); + return new InvocationMatcher(newMatcher(qualifierMatcher), methodName, Collections.emptyList()); } List argMatchers = new ArrayList<>(); i = parseArgList(sig, i, argMatchers); if (i != sig.length()) { throw new IllegalArgumentException("Not a valid signature " + sig); } - return new MethodSigMatcher(newMatcher(qualifierName, false), methodName, argMatchers); + return new InvocationMatcher(newMatcher(qualifierMatcher), methodName, argMatchers); } private static int parseArgList(String sig, int i, List argMatchers) { while (i < sig.length()) { - i = parseType(sig, i, argMatchers, true); + i = parseType(sig, i, argMatchers); if (isChar(sig, i, ')')) { return i + 1; } @@ -376,7 +397,7 @@ public final class TypeTestUtil { return i < source.length() && source.charAt(i) == c; } - private static int parseType(String source, int i, List result, boolean exact) { + private static int parseType(String source, int i, List result) { final int start = i; while (i < source.length() && (Character.isJavaIdentifierPart(source.charAt(i)) || source.charAt(i) == '.')) { @@ -384,28 +405,26 @@ public final class TypeTestUtil { } String name = source.substring(start, i); AssertionUtil.assertValidJavaBinaryName(name); - result.add(newMatcher(name, exact)); + result.add(newMatcher(name)); return i; } - private static TypeMatcher newMatcher(String name, boolean exact) { - return "_".equals(name) ? TypeMatcher.ANY : new TypeMatcher(name, exact); + private static TypeMatcher newMatcher(String name) { + return "_".equals(name) ? TypeMatcher.ANY : new TypeMatcher(name); } private static final class TypeMatcher { /** Matches any type. */ - public static final TypeMatcher ANY = new TypeMatcher(null, false); + public static final TypeMatcher ANY = new TypeMatcher(null); final @Nullable String name; - private final boolean exact; - private TypeMatcher(@Nullable String name, boolean exact) { + private TypeMatcher(@Nullable String name) { this.name = name; - this.exact = exact; } - boolean matches(JTypeMirror type) { + boolean matches(JTypeMirror type, boolean exact) { if (name == null) { return true; } From f0656c181d9d4222bef09b85eb264d55ed5e3ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 12:20:24 +0100 Subject: [PATCH 108/299] Convert some rules to XPath rules --- .../rule/performance/StringToStringRule.java | 67 ------------------- .../UseStringBufferLengthRule.java | 55 --------------- .../pmd/lang/java/types/TypeTestUtil.java | 5 +- .../resources/category/java/performance.xml | 21 +++++- 4 files changed, 22 insertions(+), 126 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringToStringRule.java delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringToStringRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringToStringRule.java deleted file mode 100644 index 4f150c8faa..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/StringToStringRule.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import org.checkerframework.checker.nullness.qual.NonNull; - -import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.rule.RuleTargetSelector; - -/** - * Finds toString() call on String object. - * - * Note: due to an issue with type resolution, this implementation doesn't detect cases when toString() - * call is chained to a method returning String which is not declared in the class having the call or the call - * arguments are not of the exact same type as method parameters are, excluding the case when method name and - * number of it's parameters is enough to identify the method. Example: - *
{@code
- *    class A {
- *         public String str() {
- *            return "exampleStr";
- *         }
- *    }
- *    class B {
- *        public void foo() {
- *            String s = new A().str().toString(); // not detected because str() is from another class
- *            s = getString().toString(); // detected
- *            s = getData(new FileInputStream()).toString(); // detected because of argument type
- *            s = getData(new Integer(4), new Integer(5)).toString(); // detected because of unique args count
- *        }
- *        public String getString() {
- *            return "exampleStr";
- *        }
- *        public String getData(InputStream is) {
- *            return "argsResolutionIssueExample";
- *        }
- *        public int getData(String s) {
- *            return 0;
- *        }
- *        public String getData(Number a, Number b) {
- *            return "uniqueArgsCountExample";
- *        }
- *    }
- *    }
- */ -public class StringToStringRule extends AbstractJavaRule { - - @Override - protected @NonNull RuleTargetSelector buildTargetSelector() { - return RuleTargetSelector.forTypes(ASTMethodCall.class); - } - - @Override - public Object visit(ASTMethodCall node, Object data) { - if ("toString".equals(node.getMethodName()) - && node.getArguments().size() == 0 - && TypeTestUtil.isA(String.class, node.getQualifier())) { - addViolation(data, node); - } - - return data; - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java deleted file mode 100644 index d2337e1c5e..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/UseStringBufferLengthRule.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.performance; - -import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; -import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; - -/** - * This rule finds places where StringBuffer.toString() is called just to see if - * the string is 0 length by either using .equals("") or toString().length(). - * - *
- * StringBuffer sb = new StringBuffer("some string");
- * if (sb.toString().equals("")) {
- *     // this is wrong
- * }
- * if (sb.length() == 0) {
- *     // this is right
- * }
- * 
- * - * @author acaplan - * @author Philip Graf - */ -public class UseStringBufferLengthRule extends AbstractJavaRulechainRule { - - public UseStringBufferLengthRule() { - super(ASTMethodCall.class); - } - - @Override - public Object visit(ASTMethodCall call, Object data) { - if ("toString".equals(call.getMethodName()) - && call.getArguments().size() == 0 - && TypeTestUtil.isA(CharSequence.class, call.getQualifier()) - && !TypeTestUtil.isA(String.class, call.getQualifier()) - && isLengthCall(call.getParent())) { - addViolation(data, call.getParent()); - } - return data; - } - - private boolean isLengthCall(JavaNode node) { - if (node instanceof ASTMethodCall) { - ASTMethodCall call = (ASTMethodCall) node; - return "length".equals(call.getMethodName()) - && call.getArguments().size() == 0; - } - return false; - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index 83edc6ef20..6c94608b61 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -14,6 +14,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.internal.util.AssertionUtil; +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTList; import net.sourceforge.pmd.lang.java.ast.InternalApiBridge; @@ -312,7 +313,9 @@ public final class TypeTestUtil { if (qualifierMatcher == TypeMatcher.ANY) { return true; } - if (node instanceof QualifiableExpression) { + if (node instanceof ASTConstructorCall) { + return qualifierMatcher.matches(((ASTConstructorCall) node).getTypeNode().getTypeMirror(), true); + } else if (node instanceof QualifiableExpression) { ASTExpression qualifier = ((QualifiableExpression) node).getQualifier(); if (qualifier != null) { return qualifierMatcher.matches(qualifier.getTypeMirror(), false); diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index 63daba98bd..c0d141b6d7 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -757,12 +757,19 @@ private String bar = new String("bar"); // just do a String bar = "bar"; language="java" since="1.0" message="Avoid calling toString() on String objects; this is unnecessary." - class="net.sourceforge.pmd.lang.java.rule.performance.StringToStringRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_performance.html#stringtostring"> Avoid calling toString() on objects already known to be string instances; this is unnecessary. 3 + + + + //MethodCall[pmd-java:matchesSig("java.lang.String#toString()")] + + + Use StringBuffer.length() to determine StringBuffer length rather than using StringBuffer.toString().equals("") or StringBuffer.toString().length() == ... + + + + //MethodCall[pmd-java:matchesSig('_#length()') + and MethodCall[pmd-java:matchesSig('java.lang.CharSequence#toString()')]] + + + 3 Date: Mon, 2 Nov 2020 13:29:18 +0100 Subject: [PATCH 109/299] Update UseAssertEqualsInsteadOfAssertTrue --- .../pmd/lang/java/types/TypeTestUtil.java | 22 +++++++++++---- .../resources/category/java/bestpractices.xml | 21 +++------------ .../resources/category/java/performance.xml | 2 +- ...seAssertEqualsInsteadOfAssertTrueTest.java | 1 - .../UseAssertEqualsInsteadOfAssertTrue.xml | 27 ++++++++++++++----- 5 files changed, 42 insertions(+), 31 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index 6c94608b61..71f9c986e1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -282,10 +282,10 @@ public final class TypeTestUtil { public static final class InvocationMatcher { final String expectedName; - final List argMatchers; + final @Nullable List argMatchers; final TypeMatcher qualifierMatcher; - InvocationMatcher(TypeMatcher qualifierMatcher, String expectedName, List argMatchers) { + InvocationMatcher(TypeMatcher qualifierMatcher, String expectedName, @Nullable List argMatchers) { this.expectedName = expectedName; this.argMatchers = argMatchers; this.qualifierMatcher = qualifierMatcher; @@ -299,7 +299,7 @@ public final class TypeTestUtil { */ public boolean matchesCall(InvocationNode node) { if (!node.getMethodName().equals(expectedName) - || ASTList.sizeOrZero(node.getArguments()) != argMatchers.size()) { + || argMatchers != null && ASTList.sizeOrZero(node.getArguments()) != argMatchers.size()) { return false; } OverloadSelectionResult info = node.getOverloadSelectionInfo(); @@ -328,6 +328,9 @@ public final class TypeTestUtil { } private boolean argsMatchOverload(JMethodSig invoc) { + if (argMatchers == null) { + return true; + } List formals = invoc.getFormalParameters(); if (invoc.getArity() != argMatchers.size()) { return false; @@ -370,8 +373,17 @@ public final class TypeTestUtil { if (isChar(sig, i, ')')) { return new InvocationMatcher(newMatcher(qualifierMatcher), methodName, Collections.emptyList()); } - List argMatchers = new ArrayList<>(); - i = parseArgList(sig, i, argMatchers); + // (_*) matches any argument list + List argMatchers; + if (isChar(sig, i, '_') + && isChar(sig, i + 1, '*') + && isChar(sig, i + 2, ')')) { + argMatchers = null; + i = i + 3; + } else { + argMatchers = new ArrayList<>(); + i = parseArgList(sig, i, argMatchers); + } if (i != sig.length()) { throw new IllegalArgumentException("Not a valid signature " + sig); } diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 089826f288..d62115b7ca 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -1555,23 +1555,10 @@ This rule detects JUnit assertions in object equality. These assertions should b diff --git a/pmd-java/src/main/resources/category/java/performance.xml b/pmd-java/src/main/resources/category/java/performance.xml index c0d141b6d7..ecdc51230d 100644 --- a/pmd-java/src/main/resources/category/java/performance.xml +++ b/pmd-java/src/main/resources/category/java/performance.xml @@ -1095,6 +1095,7 @@ public class Foo { Use StringBuffer.length() to determine StringBuffer length rather than using StringBuffer.toString().equals("") or StringBuffer.toString().length() == ... + 3 @@ -1103,7 +1104,6 @@ or StringBuffer.toString().length() == ... - 3 - TEST1 + Identity test doesn't match 0 - TEST2 + Junit 3 problem 1 - TEST3 + Overload of equal doesn't match 0 - Not a JUnit test - TEST2 - 0 + JUnit 4, even outside of @Test method + 1 - JUnit4 - TEST2 + JUnit4 - match 1 Date: Mon, 2 Nov 2020 13:38:28 +0100 Subject: [PATCH 110/299] Update UseAssertNullInsteadOfAssertTrue --- .travis/all-java.xml | 4 ++-- .../pmd/lang/java/types/TypeTestUtil.java | 6 ++--- .../resources/category/java/bestpractices.xml | 24 +++++-------------- .../UseAssertNullInsteadOfAssertTrueTest.java | 1 - .../xml/UseAssertNullInsteadOfAssertTrue.xml | 6 +++++ 5 files changed, 17 insertions(+), 24 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 212e305686..6e9386af3b 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -54,8 +54,8 @@ - - + + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index 71f9c986e1..e6961f3375 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -281,12 +281,12 @@ public final class TypeTestUtil { */ public static final class InvocationMatcher { - final String expectedName; + final @Nullable String expectedName; final @Nullable List argMatchers; final TypeMatcher qualifierMatcher; InvocationMatcher(TypeMatcher qualifierMatcher, String expectedName, @Nullable List argMatchers) { - this.expectedName = expectedName; + this.expectedName = expectedName.equals("_") ? null : expectedName; this.argMatchers = argMatchers; this.qualifierMatcher = qualifierMatcher; } @@ -298,7 +298,7 @@ public final class TypeTestUtil { * one mentioned by the qualifier matcher. */ public boolean matchesCall(InvocationNode node) { - if (!node.getMethodName().equals(expectedName) + if (expectedName != null && !node.getMethodName().equals(expectedName) || argMatchers != null && ASTList.sizeOrZero(node.getArguments()) != argMatchers.size()) { return false; } diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index d62115b7ca..9304f79708 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -1591,24 +1591,12 @@ more specific methods, like assertNull, assertNotNull. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertNullInsteadOfAssertTrueTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertNullInsteadOfAssertTrueTest.java index c67101252c..a593a9e0ff 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertNullInsteadOfAssertTrueTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertNullInsteadOfAssertTrueTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseAssertNullInsteadOfAssertTrueTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertNullInsteadOfAssertTrue.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertNullInsteadOfAssertTrue.xml index 5715ae81df..fdd79d4e26 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertNullInsteadOfAssertTrue.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertNullInsteadOfAssertTrue.xml @@ -24,6 +24,7 @@ public class Foo extends TestCase { import junit.framework.TestCase; public class Foo extends TestCase { public void test1() { + Object a, b; assertFalse(a != null); } } @@ -37,6 +38,7 @@ public class Foo extends TestCase { import junit.framework.TestCase; public class Foo extends TestCase { public void test1() { + Object a, b; assertTrue(a == b); } } @@ -49,6 +51,7 @@ public class Foo extends TestCase { JUnit 4 - assertTrue with null 1 Date: Mon, 2 Nov 2020 13:40:33 +0100 Subject: [PATCH 111/299] Update UseAssertSameInsteadOfAssertTrue --- .travis/all-java.xml | 2 +- .../resources/category/java/bestpractices.xml | 24 +++++-------------- .../UseAssertSameInsteadOfAssertTrueTest.java | 1 - .../xml/UseAssertSameInsteadOfAssertTrue.xml | 1 + 4 files changed, 8 insertions(+), 20 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 6e9386af3b..f7b1d1e105 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -56,7 +56,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 9304f79708..251f1aec0f 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -1631,24 +1631,12 @@ by more specific methods, like assertSame, assertNotSame. diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertSameInsteadOfAssertTrueTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertSameInsteadOfAssertTrueTest.java index ba0d3684bf..e874a309b1 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertSameInsteadOfAssertTrueTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertSameInsteadOfAssertTrueTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseAssertSameInsteadOfAssertTrueTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertSameInsteadOfAssertTrue.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertSameInsteadOfAssertTrue.xml index b9044439aa..5a0a74559e 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertSameInsteadOfAssertTrue.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertSameInsteadOfAssertTrue.xml @@ -100,6 +100,7 @@ public class Foo { 1 Date: Mon, 2 Nov 2020 13:48:30 +0100 Subject: [PATCH 112/299] Update UseAssertTrueInsteadOfAssertEquals --- .travis/all-java.xml | 2 +- .../main/resources/category/java/bestpractices.xml | 13 ++++++------- .../UseAssertTrueInsteadOfAssertEqualsTest.java | 1 - .../xml/UseAssertTrueInsteadOfAssertEquals.xml | 4 ++++ 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index f7b1d1e105..8da97ccd1c 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -57,7 +57,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 251f1aec0f..f20e3682ac 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -1668,13 +1668,12 @@ When asserting a value is the same as a literal or Boxed boolean, use assertTrue diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertTrueInsteadOfAssertEqualsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertTrueInsteadOfAssertEqualsTest.java index a445fbd465..cb8ded71bf 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertTrueInsteadOfAssertEqualsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UseAssertTrueInsteadOfAssertEqualsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class UseAssertTrueInsteadOfAssertEqualsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertTrueInsteadOfAssertEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertTrueInsteadOfAssertEquals.xml index 5f2b63749a..13358b5119 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertTrueInsteadOfAssertEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseAssertTrueInsteadOfAssertEquals.xml @@ -8,6 +8,7 @@ JUnit Test contains assertEquals on other than boolean literal 0 JUnit Test contains assertEquals on boolean literal 5 JUnit Test contains assertEquals with Boxed booleans 8 JUnit Test contains assertEquals with Boxed booleans as param 0 Date: Mon, 2 Nov 2020 16:19:42 +0100 Subject: [PATCH 113/299] Update JUnitTestContainsTooManyAsserts --- .travis/all-java.xml | 2 +- .../resources/category/java/bestpractices.xml | 21 ++++++++++--------- .../JUnitTestContainsTooManyAssertsTest.java | 1 - .../xml/JUnitTestContainsTooManyAsserts.xml | 13 +++++++++--- 4 files changed, 22 insertions(+), 15 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 8da97ccd1c..025250c802 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -32,7 +32,7 @@ - + diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index f20e3682ac..733945ad36 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -809,16 +809,17 @@ This rule checks for JUnit4, JUnit5 and TestNG Tests, as well as methods startin $maximumAsserts] +//MethodDeclaration[ + @Name[matches(.,'^test')] + or pmd-java:hasAnnotation('org.junit.Test') + or pmd-java:hasAnnotation('org.junit.jupiter.api.Test') + or pmd-java:hasAnnotation('org.junit.jupiter.api.RepeatedTest') + or pmd-java:hasAnnotation('org.junit.jupiter.api.TestFactory') + or pmd-java:hasAnnotation('org.junit.jupiter.api.TestTemplate') + or pmd-java:hasAnnotation('org.junit.jupiter.params.ParameterizedTest') + or pmd-java:hasAnnotation('org.testng.annotations.Test') + ] + [Block[count(//MethodCall[@MethodName[matches(.,'^assert')]]) > $maximumAsserts]] ]]> diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsTest.java index 12cd714f75..76656a65d0 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class JUnitTestContainsTooManyAssertsTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestContainsTooManyAsserts.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestContainsTooManyAsserts.xml index ba19d3eea6..eb7ed6d988 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestContainsTooManyAsserts.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestContainsTooManyAsserts.xml @@ -8,6 +8,7 @@ JUnit 3 Test contains no assert 0 JUnit 4 Test contains no assert 0 JUnit 3 Test contains one assert 0 JUnit 4 Test contains one assert 0 JUnit 4 Test contains more than one assert 1 2 0 2 0 Date: Mon, 2 Nov 2020 16:56:10 +0100 Subject: [PATCH 114/299] Update JUnitTestsShouldIncludeAssert --- .travis/all-java.xml | 2 +- .../lang/java/ast/ASTImportDeclaration.java | 4 +- .../pmd/lang/java/rule/AbstractJUnitRule.java | 33 ++- .../JUnitTestsShouldIncludeAssertRule.java | 218 +++--------------- .../bestpractices/JUnitUseExpectedRule.java | 2 +- .../TestClassWithoutTestCasesRule.java | 2 +- .../JUnitTestsShouldIncludeAssertTest.java | 1 - .../xml/JUnitTestsShouldIncludeAssert.xml | 5 +- 8 files changed, 60 insertions(+), 207 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 025250c802..da556828fd 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -33,7 +33,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTImportDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTImportDeclaration.java index aa99e7b784..48fd83e886 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTImportDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTImportDeclaration.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.java.ast; +import org.checkerframework.checker.nullness.qual.NonNull; + /** * Represents an import declaration in a Java file. * @@ -66,7 +68,7 @@ public final class ASTImportDeclaration extends AbstractJavaNode implements ASTT * Returns the full name of the import. For on-demand imports, this is the name without * the final dot and asterisk. */ - public String getImportedName() { + public @NonNull String getImportedName() { return super.getImage(); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java index 874331fbf2..15e017d6af 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java @@ -55,42 +55,35 @@ public abstract class AbstractJUnitRule extends AbstractJavaRule { } private boolean isTestNgClass(ASTCompilationUnit node) { - List imports = node.findDescendantsOfType(ASTImportDeclaration.class); - for (ASTImportDeclaration i : imports) { - if (i.getImportedName() != null && i.getImportedName().startsWith("org.testng")) { - return true; - } - } - return false; + return node.children(ASTImportDeclaration.class) + .any(i -> i.getImportedName().startsWith("org.testng")); } - public boolean isJUnitMethod(ASTMethodDeclaration method, Object data) { - if (method.isAbstract() || method.isNative() || method.isStatic()) { + public boolean isJUnitMethod(ASTMethodDeclaration method) { + if (method.isStatic() || method.getBody() == null) { return false; // skip various inapplicable method variations } - if (!isJUnit5Class && !method.isPublic()) { - // junit5 class doesn't require test methods to be public anymore - return false; - } - boolean result = false; - result |= isJUnit3Method(method); - result |= isJUnit4Method(method); - result |= isJUnit5Method(method); + result = result || isJUnit5Method(method); + result = result || isJUnit4Method(method); + result = result || isJUnit3Method(method); return result; } private boolean isJUnit4Method(ASTMethodDeclaration method) { - return isJUnit4Class && doesNodeContainJUnitAnnotation(method.getParent(), JUNIT4_CLASS_NAME); + return method.isAnnotationPresent(JUNIT4_CLASS_NAME) && method.isPublic(); } private boolean isJUnit5Method(ASTMethodDeclaration method) { - return isJUnit5Class && doesNodeContainJUnitAnnotation(method.getParent(), JUNIT5_CLASS_NAME); + return method.isAnnotationPresent(JUNIT5_CLASS_NAME); } private boolean isJUnit3Method(ASTMethodDeclaration method) { - return isJUnit3Class && method.isVoid() && method.getName().startsWith("test"); + return TypeTestUtil.isA("junit.framework.TestCase", method.getEnclosingType()) + && method.isVoid() + && method.isPublic() + && method.getName().startsWith("test"); } private boolean isJUnit3Class(ASTCompilationUnit node) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index 385509d3e9..484bfb4ebd 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -4,115 +4,49 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import static net.sourceforge.pmd.util.CollectionUtil.setOf; + +import java.util.Set; + +import org.checkerframework.checker.nullness.qual.NonNull; -import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTBlock; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; -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.ASTReferenceType; -import net.sourceforge.pmd.lang.java.ast.ASTStatementExpression; import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; +import net.sourceforge.pmd.lang.java.types.JTypeMirror; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.symboltable.NameDeclaration; -import net.sourceforge.pmd.lang.symboltable.NameOccurrence; -import net.sourceforge.pmd.lang.symboltable.Scope; +import net.sourceforge.pmd.lang.rule.RuleTargetSelector; public class JUnitTestsShouldIncludeAssertRule extends AbstractJUnitRule { + + private static final Set MOCKITO = setOf("org.mockito.Mockito"); + private static final Set ASSERT_CONTAINERS = setOf("org.junit.Assert", + "org.junit.jupiter.api.Assertions", + "org.hamcrest.MatcherAssert", + "junit.framework.TestCase"); + @Override - public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - if (node.isInterface()) { - return data; - } - return super.visit(node, data); + protected @NonNull RuleTargetSelector buildTargetSelector() { + return RuleTargetSelector.forTypes(ASTMethodDeclaration.class); } @Override public Object visit(ASTMethodDeclaration method, Object data) { - if (isJUnitMethod(method, data)) { - if (!isExpectAnnotated(method)) { - Map variables = getVariables(method); - - Scope classScope = method.getScope().getParent(); - Map> expectables = getRuleAnnotatedExpectedExceptions(classScope); - - if (!containsExpectOrAssert(method.getBody(), expectables, variables)) { - addViolation(data, method); - } - } + ASTBlock body = method.getBody(); + if (body != null + && isJUnitMethod(method) + && !isExpectAnnotated(method) + && body.descendants(ASTMethodCall.class).none(this::isAssertCall)) { + addViolation(data, method); } return data; } - private boolean containsExpectOrAssert(Node n, - Map> expectables, - Map variables) { - if (n instanceof ASTStatementExpression) { - if (isExpectStatement((ASTStatementExpression) n, expectables) - || isAssertOrFailStatement((ASTStatementExpression) n) - || isHamcrestAssert((ASTStatementExpression) n) - || isVerifyStatement((ASTStatementExpression) n) - || isSoftAssertionStatement((ASTStatementExpression) n, variables)) { - return true; - } - } else { - for (int i = 0; i < n.getNumChildren(); i++) { - Node c = n.getChild(i); - if (containsExpectOrAssert(c, expectables, variables)) { - return true; - } - } - } - return false; - } - - private Map getVariables(ASTMethodDeclaration method) { - Map variables = new HashMap<>(); - for (VariableNameDeclaration vnd : method.getScope().getDeclarations(VariableNameDeclaration.class).keySet()) { - variables.put(vnd.getName(), vnd); - } - return variables; - } - - /** - * Gets a list of NameDeclarations for all the fields that have type - * ExpectedException and have a Rule annotation. - * - * @param classScope - * The class scope to search for - * @return See description - */ - private Map> getRuleAnnotatedExpectedExceptions(Scope classScope) { - Map> result = new HashMap<>(); - Map> decls = classScope.getDeclarations(); - - for (Map.Entry> entry : decls.entrySet()) { - Node parent = entry.getKey().getNode().getParent().getParent().getParent(); - if (parent.getFirstChildOfType(ASTFieldDeclaration.class) != null) { - ASTAnnotation annot = parent.getFirstDescendantOfType(ASTAnnotation.class); - if (annot == null || !TypeTestUtil.isA("org.junit.Rule", annot)) { - continue; - } - - Node type = parent.getFirstDescendantOfType(ASTReferenceType.class); - if (!"ExpectedException".equals(type.getChild(0).getImage())) { - continue; - } - result.put(entry.getKey().getName(), entry.getValue()); - } - } - return result; - } - /** * Tells if the node contains a Test annotation with an expected exception. */ @@ -124,96 +58,18 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJUnitRule { } - private String getMethodCallNameOrNull(ASTStatementExpression expression) { - if (expression != null) { - ASTPrimaryExpression pe = expression.getFirstChildOfType(ASTPrimaryExpression.class); - if (pe != null) { - Node name = pe.getFirstDescendantOfType(ASTName.class); - if (name != null) { - return name.getImage(); - } - } - } - return null; + private boolean isAssertCall(ASTMethodCall call) { + String name = call.getMethodName(); + return name.equals("expect") && TypeTestUtil.isA("org.junit.rules.ExpectedException", call.getQualifier()) + || name.equals("assertAll") && TypeTestUtil.isA("org.assertj.core.api.SoftAssertions", call.getQualifier()) + || name.equals("verify") && isCallOnType(call, MOCKITO) + || (name.startsWith("assert") || name.equals("fail")) && isCallOnType(call, ASSERT_CONTAINERS); } - /** - * Tells if the expression is an Hamcrest assert - */ - private boolean isHamcrestAssert(ASTStatementExpression expression) { - String img = getMethodCallNameOrNull(expression); - return "assertThat".equals(img) || "MatcherAssert.assertThat".equals(img); - } - - /** - * Tells if the expression is an assert statement or not. - */ - private boolean isAssertOrFailStatement(ASTStatementExpression expression) { - String img = getMethodCallNameOrNull(expression); - return img != null && (img.startsWith("assert") || img.startsWith("fail") - || img.startsWith("Assert.assert") || img.startsWith("Assert.fail")); - } - - /** - * Tells if the expression is verify statement or not - */ - private boolean isVerifyStatement(ASTStatementExpression expression) { - String img = getMethodCallNameOrNull(expression); - return img != null && (img.startsWith("verify") || img.startsWith("Mockito.verify")); - } - - private boolean isExpectStatement(ASTStatementExpression expression, - Map> expectables) { - ASTPrimaryExpression pe = expression.getFirstChildOfType(ASTPrimaryExpression.class); - if (pe != null) { - ASTPrimaryPrefix primaryPrefix = pe.getFirstChildOfType(ASTPrimaryPrefix.class); - Node name = pe.getFirstDescendantOfType(ASTName.class); - if (!primaryPrefix.usesThisModifier() && name != null) { - String[] parts = name.getImage().split("\\."); - if (parts.length >= 2) { - String varname = parts[0]; - String methodName = parts[1]; - if (expectables.containsKey(varname) && "expect".equals(methodName)) { - return true; - } - } - } else if (primaryPrefix.usesThisModifier()) { - List primarySuffixes = pe.findChildrenOfType(ASTPrimarySuffix.class); - if (primarySuffixes.size() >= 2) { - String varname = primarySuffixes.get(0).getImage(); - String methodName = primarySuffixes.get(1).getImage(); - if (expectables.containsKey(varname) && "expect".equals(methodName)) { - return true; - } - } - } - } - return false; - } - - private boolean isSoftAssertionStatement(ASTStatementExpression expression, - Map variables) { - if (expression != null) { - ASTPrimaryExpression pe = expression.getFirstChildOfType(ASTPrimaryExpression.class); - if (pe != null) { - Node name = pe.getFirstDescendantOfType(ASTName.class); - if (name != null) { - String img = name.getImage(); - if (!img.contains(".")) { - return false; - } - String[] tokens = img.split("\\."); - String methodName = tokens[1]; - boolean methodIsAssertAll = "assertAll".equals(methodName); - - String varName = tokens[0]; - boolean variableTypeIsSoftAssertion = variables.containsKey(varName) - && TypeTestUtil.isA("org.assertj.core.api.AbstractSoftAssertions", variables.get(varName).getDeclaratorId()); - - return methodIsAssertAll && variableTypeIsSoftAssertion; - } - } - } - return false; + private boolean isCallOnType(ASTMethodCall call, Set qualifierTypes) { + JTypeMirror declaring = call.getMethodType().getDeclaringType(); + JTypeDeclSymbol sym = declaring.getSymbol(); + String binaryName = !(sym instanceof JClassSymbol) ? null : ((JClassSymbol) sym).getBinaryName(); + return qualifierTypes.contains(binaryName); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java index f4c80dee9e..8f08e22e57 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java @@ -56,7 +56,7 @@ public class JUnitUseExpectedRule extends AbstractJUnitRule { } } if (child instanceof ASTMethodDeclaration) { - boolean isJUnitMethod = isJUnitMethod((ASTMethodDeclaration) child, data); + boolean isJUnitMethod = isJUnitMethod((ASTMethodDeclaration) child); if (inAnnotation || isJUnitMethod) { List found = new ArrayList<>((List) visit((ASTMethodDeclaration) child, data)); for (Node name : found) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java index 6900c018fc..be3f2a82ba 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java @@ -23,7 +23,7 @@ public class TestClassWithoutTestCasesRule extends AbstractJUnitRule { if (m != null) { for (ASTMethodDeclaration md : m) { - if (isJUnitMethod(md, data)) { + if (isJUnitMethod(md)) { testsFound = true; break; } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertTest.java index 307970a3c7..373e30c109 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class JUnitTestsShouldIncludeAssertTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml index d94456d6e0..3f20347c1a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml @@ -261,9 +261,10 @@ public class Foo_Test #285 Issues with @Rule annotation and ExpectedException 1 - 7 + 8 0 0 Date: Mon, 2 Nov 2020 17:01:05 +0100 Subject: [PATCH 115/299] Update JUnitSpelling --- .travis/all-java.xml | 2 +- .../pmd/lang/java/rule/AbstractJUnitRule.java | 6 +-- .../rule/errorprone/JUnitSpellingRule.java | 41 +++++++++++-------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index da556828fd..e84b18cac6 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -233,7 +233,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java index 15e017d6af..1b3632bbc8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java @@ -24,9 +24,9 @@ import net.sourceforge.pmd.lang.java.types.TypeTestUtil; @InternalApi public abstract class AbstractJUnitRule extends AbstractJavaRule { - protected static final String JUNIT3_CLASS_NAME = "junit.framework.TestCase"; - protected static final String JUNIT4_CLASS_NAME = "org.junit.Test"; - protected static final String JUNIT5_CLASS_NAME = "org.junit.jupiter.api.Test"; + public static final String JUNIT3_CLASS_NAME = "junit.framework.TestCase"; + public static final String JUNIT4_CLASS_NAME = "org.junit.Test"; + public static final String JUNIT5_CLASS_NAME = "org.junit.jupiter.api.Test"; protected boolean isJUnit3Class; protected boolean isJUnit4Class; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java index 573b5a1c6d..5944d0a237 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java @@ -5,28 +5,37 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; +import static net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule.JUNIT3_CLASS_NAME; -public class JUnitSpellingRule extends AbstractJUnitRule { +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; + +public class JUnitSpellingRule extends AbstractJavaRulechainRule { + + public JUnitSpellingRule() { + super(ASTClassOrInterfaceDeclaration.class); + } @Override - public Object visit(ASTMethodDeclaration node, Object data) { - if (isJUnit5Class || isJUnit4Class) { - return super.visit(node, data); + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (node.isRegularClass() && TypeTestUtil.isA(JUNIT3_CLASS_NAME, node)) { + node.getDeclarations() + .filterIs(ASTMethodDeclaration.class) + .filter(this::isViolation) + .forEach(it -> addViolation(data, it)); } + return null; + } - if (node.getArity() != 0) { - return super.visit(node, data); + private boolean isViolation(ASTMethodDeclaration method) { + if (method.getArity() != 0) { + return false; } + String name = method.getName(); + return !"setUp".equals(name) && "setup".equalsIgnoreCase(name) + || !"tearDown".equals(name) && "teardown".equalsIgnoreCase(name); - String name = node.getName(); - if (!"setUp".equals(name) && "setup".equalsIgnoreCase(name)) { - addViolation(data, node); - } - if (!"tearDown".equals(name) && "teardown".equalsIgnoreCase(name)) { - addViolation(data, node); - } - return super.visit(node, data); } } From f7a06953013713db7f549bc33c12c852fc43c5dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 17:13:21 +0100 Subject: [PATCH 116/299] Update JUnitUseExpected --- .travis/all-java.xml | 2 +- .../pmd/lang/java/rule/AbstractJUnitRule.java | 8 +- .../JUnitTestsShouldIncludeAssertRule.java | 14 ++- .../bestpractices/JUnitUseExpectedRule.java | 101 +++++------------- .../bestpractices/JUnitUseExpectedTest.java | 1 - 5 files changed, 40 insertions(+), 86 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index e84b18cac6..67562faf5e 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -34,7 +34,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java index 1b3632bbc8..4b413b9652 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java @@ -59,7 +59,7 @@ public abstract class AbstractJUnitRule extends AbstractJavaRule { .any(i -> i.getImportedName().startsWith("org.testng")); } - public boolean isJUnitMethod(ASTMethodDeclaration method) { + public static boolean isJUnitMethod(ASTMethodDeclaration method) { if (method.isStatic() || method.getBody() == null) { return false; // skip various inapplicable method variations } @@ -71,15 +71,15 @@ public abstract class AbstractJUnitRule extends AbstractJavaRule { return result; } - private boolean isJUnit4Method(ASTMethodDeclaration method) { + private static boolean isJUnit4Method(ASTMethodDeclaration method) { return method.isAnnotationPresent(JUNIT4_CLASS_NAME) && method.isPublic(); } - private boolean isJUnit5Method(ASTMethodDeclaration method) { + private static boolean isJUnit5Method(ASTMethodDeclaration method) { return method.isAnnotationPresent(JUNIT5_CLASS_NAME); } - private boolean isJUnit3Method(ASTMethodDeclaration method) { + private static boolean isJUnit3Method(ASTMethodDeclaration method) { return TypeTestUtil.isA("junit.framework.TestCase", method.getEnclosingType()) && method.isVoid() && method.isPublic() diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index 484bfb4ebd..1ca0b65f72 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -4,24 +4,23 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; +import static net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule.JUNIT4_CLASS_NAME; import static net.sourceforge.pmd.util.CollectionUtil.setOf; import java.util.Set; -import org.checkerframework.checker.nullness.qual.NonNull; - import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; import net.sourceforge.pmd.lang.java.types.JTypeMirror; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.rule.RuleTargetSelector; -public class JUnitTestsShouldIncludeAssertRule extends AbstractJUnitRule { +public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule { private static final Set MOCKITO = setOf("org.mockito.Mockito"); @@ -30,16 +29,15 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJUnitRule { "org.hamcrest.MatcherAssert", "junit.framework.TestCase"); - @Override - protected @NonNull RuleTargetSelector buildTargetSelector() { - return RuleTargetSelector.forTypes(ASTMethodDeclaration.class); + public JUnitTestsShouldIncludeAssertRule() { + super(ASTMethodDeclaration.class); } @Override public Object visit(ASTMethodDeclaration method, Object data) { ASTBlock body = method.getBody(); if (body != null - && isJUnitMethod(method) + && AbstractJUnitRule.isJUnitMethod(method) && !isExpectAnnotated(method) && body.descendants(ASTMethodCall.class).none(this::isAssertCall)) { addViolation(data, method); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java index 8f08e22e57..f82529d4b2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java @@ -4,20 +4,16 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.util.ArrayList; -import java.util.List; +import static net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule.isJUnitMethod; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTBlock; -import net.sourceforge.pmd.lang.java.ast.ASTBlockStatement; -import net.sourceforge.pmd.lang.java.ast.ASTCatchClause; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBodyDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; +import net.sourceforge.pmd.lang.java.ast.ASTStatement; import net.sourceforge.pmd.lang.java.ast.ASTThrowStatement; import net.sourceforge.pmd.lang.java.ast.ASTTryStatement; -import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; /** * This rule finds code like this: @@ -41,75 +37,36 @@ import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; * @author acaplan * */ -public class JUnitUseExpectedRule extends AbstractJUnitRule { +public class JUnitUseExpectedRule extends AbstractJavaRulechainRule { - @Override - public Object visit(ASTClassOrInterfaceBodyDeclaration node, Object data) { - boolean inAnnotation = false; - for (int i = 0; i < node.getNumChildren(); i++) { - Node child = node.getChild(i); - if (child instanceof ASTAnnotation) { - ASTName annotationName = child.getFirstDescendantOfType(ASTName.class); - if ("Test".equals(annotationName.getImage())) { - inAnnotation = true; - continue; - } - } - if (child instanceof ASTMethodDeclaration) { - boolean isJUnitMethod = isJUnitMethod((ASTMethodDeclaration) child); - if (inAnnotation || isJUnitMethod) { - List found = new ArrayList<>((List) visit((ASTMethodDeclaration) child, data)); - for (Node name : found) { - addViolation(data, name); - } - } - } - inAnnotation = false; - } - - return super.visit(node, data); + public JUnitUseExpectedRule() { + super(ASTMethodDeclaration.class); } @Override public Object visit(ASTMethodDeclaration node, Object data) { - List catches = node.findDescendantsOfType(ASTTryStatement.class); - List found = new ArrayList<>(); - if (catches.isEmpty()) { - return found; - } - for (ASTTryStatement trySt : catches) { - ASTCatchClause cStatement = getCatch(trySt); - if (cStatement != null) { - ASTBlock block = (ASTBlock) cStatement.getChild(1); - if (block.getNumChildren() != 0) { - continue; - } - List blocks = trySt.getChild(0).findDescendantsOfType(ASTBlockStatement.class); - if (blocks.isEmpty()) { - continue; - } - ASTBlockStatement st = blocks.get(blocks.size() - 1); - ASTName name = st.getFirstDescendantOfType(ASTName.class); - if (name != null && st.equals(name.getNthParent(5)) && "fail".equals(name.getImage())) { - found.add(name); - continue; - } - ASTThrowStatement th = st.getFirstDescendantOfType(ASTThrowStatement.class); - if (th != null && st.equals(th.getNthParent(2))) { - found.add(th); - continue; - } - } - } - return found; - } - - private ASTCatchClause getCatch(Node n) { - for (int i = 0; i < n.getNumChildren(); i++) { - if (n.getChild(i) instanceof ASTCatchClause) { - return (ASTCatchClause) n.getChild(i); - } + ASTBlock body = node.getBody(); + if (body != null && isJUnitMethod(node)) { + body.descendants(ASTTryStatement.class) + .filter(this::isWeirdTry) + .forEach(it -> addViolation(data, it)); } return null; } + + private boolean isWeirdTry(ASTTryStatement tryStmt) { + ASTStatement lastStmt = tryStmt.getBody().getLastChild(); + return (lastStmt instanceof ASTThrowStatement + || lastStmt instanceof ASTExpressionStatement && isFailStmt((ASTExpressionStatement) lastStmt)) + && tryStmt.getCatchClauses().any(it -> it.getBody().size() == 0); + } + + private boolean isFailStmt(ASTExpressionStatement stmt) { + if (stmt.getExpr() instanceof ASTMethodCall) { + ASTMethodCall expr = (ASTMethodCall) stmt.getExpr(); + return expr.getMethodName().equals("fail"); + } + return false; + } + } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedTest.java index 4419c0a53c..6cef9b5e22 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class JUnitUseExpectedTest extends PmdRuleTst { // no additional unit tests } From f13c8c51b89fc443c86dec43ae2a59dd048db7e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 2 Nov 2020 17:44:48 +0100 Subject: [PATCH 117/299] Update JUnitAssertionsShouldIncludeMessage --- .travis/all-java.xml | 2 +- ...nitAssertionsShouldIncludeMessageRule.java | 107 +++++------------- .../JUnitTestsShouldIncludeAssertRule.java | 12 +- .../pmd/lang/java/types/TypeTestUtil.java | 30 +++-- ...nitAssertionsShouldIncludeMessageTest.java | 1 - .../JUnitAssertionsShouldIncludeMessage.xml | 17 +-- 6 files changed, 73 insertions(+), 96 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 67562faf5e..0fff277218 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -31,7 +31,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java index 6ae4943d11..c904f758c7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java @@ -4,93 +4,48 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import java.util.ArrayList; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; + 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.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryExpression; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil.InvocationMatcher; -public class JUnitAssertionsShouldIncludeMessageRule extends AbstractJUnitRule { +public class JUnitAssertionsShouldIncludeMessageRule extends AbstractJavaRulechainRule { - private class AssertionCall { - private final int argumentsCount; - private final String assertionName; + private final List checks = + listOf( + InvocationMatcher.parse("_", "assertEquals(_,_)"), - AssertionCall(String assertionName, int argumentsCount) { - this.argumentsCount = argumentsCount; - this.assertionName = assertionName; - } + InvocationMatcher.parse("_", "assertTrue(_)"), + InvocationMatcher.parse("_", "assertFalse(_)"), + InvocationMatcher.parse("_", "assertSame(_,_)"), + InvocationMatcher.parse("_", "assertNotSame(_,_)"), + InvocationMatcher.parse("_", "assertNull(_)"), + InvocationMatcher.parse("_", "assertNotNull(_)"), - public void check(Object ctx, ASTArguments node) { - if (node.size() == argumentsCount - && node.getNthParent(2) instanceof ASTPrimaryExpression) { - ASTPrimaryPrefix primaryPrefix = node.getNthParent(2).getFirstChildOfType(ASTPrimaryPrefix.class); - - if (primaryPrefix != null) { - ASTName name = primaryPrefix.getFirstChildOfType(ASTName.class); - - if (name != null && name.hasImageEqualTo(this.assertionName)) { - if (isException(node)) { - return; - } - JUnitAssertionsShouldIncludeMessageRule.this.addViolation(ctx, name); - } - } - } - } - - protected boolean isException(ASTArguments node) { - return false; - } - } - - private List checks = new ArrayList<>(); + InvocationMatcher.parse("_", "assertArrayEquals(_,_)"), + InvocationMatcher.parse("_", "assertThat(_,_)"), + InvocationMatcher.parse("_", "fail()"), + InvocationMatcher.parse("_", "assertEquals(float,float,float)"), + InvocationMatcher.parse("_", "assertEquals(double,double,double)") + ); public JUnitAssertionsShouldIncludeMessageRule() { - checks.add(new AssertionCall("assertArrayEquals", 2)); - checks.add(new AssertionCall("assertEquals", 2)); - checks.add(new AssertionCall("assertFalse", 1)); - checks.add(new AssertionCall("assertNotNull", 1)); - checks.add(new AssertionCall("assertNotSame", 2)); - checks.add(new AssertionCall("assertNull", 1)); - checks.add(new AssertionCall("assertSame", 2)); - checks.add(new AssertionCall("assertThat", 2)); - checks.add(new AssertionCall("assertTrue", 1)); - checks.add(new AssertionCall("fail", 0)); - - checks.add(new AssertionCall("assertEquals", 3) { - @Override - protected boolean isException(ASTArguments node) { - // consider the top-level expressions of the arguments: Arguments/ArgumentList/Expression - ASTArgumentList argumentList = node.getFirstChildOfType(ASTArgumentList.class); - List arguments = argumentList.findChildrenOfType(ASTExpression.class); - boolean isExceptionJunit4 = isStringTypeOrNull(arguments.get(0)); - boolean isExceptionJunit5 = isStringTypeOrNull(arguments.get(2)); - - return isExceptionJunit4 || isExceptionJunit5; - } - }); + super(ASTMethodCall.class); } @Override - public Object visit(ASTArguments node, Object data) { - for (AssertionCall call : checks) { - call.check(data, node); + public Object visit(ASTMethodCall node, Object data) { + if (JUnitTestsShouldIncludeAssertRule.isCallOnAssertionContainer(node)) { + for (InvocationMatcher check : checks) { + if (check.matchesCall(node)) { + addViolation(data, node); + break; + } + } } - return super.visit(node, data); - } - - /** - * @param node - * the node to check - * @return {@code true} if node's type is String or null, otherwise {@code false} - */ - private boolean isStringTypeOrNull(ASTExpression node) { - return node.getType() == String.class || node.getType() == null; + return null; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index 1ca0b65f72..ed3b9cbd74 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -39,7 +39,7 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule if (body != null && AbstractJUnitRule.isJUnitMethod(method) && !isExpectAnnotated(method) - && body.descendants(ASTMethodCall.class).none(this::isAssertCall)) { + && body.descendants(ASTMethodCall.class).none(JUnitTestsShouldIncludeAssertRule::isAssertCall)) { addViolation(data, method); } return data; @@ -56,15 +56,19 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule } - private boolean isAssertCall(ASTMethodCall call) { + private static boolean isAssertCall(ASTMethodCall call) { String name = call.getMethodName(); return name.equals("expect") && TypeTestUtil.isA("org.junit.rules.ExpectedException", call.getQualifier()) || name.equals("assertAll") && TypeTestUtil.isA("org.assertj.core.api.SoftAssertions", call.getQualifier()) || name.equals("verify") && isCallOnType(call, MOCKITO) - || (name.startsWith("assert") || name.equals("fail")) && isCallOnType(call, ASSERT_CONTAINERS); + || (name.startsWith("assert") || name.equals("fail")) && isCallOnAssertionContainer(call); } - private boolean isCallOnType(ASTMethodCall call, Set qualifierTypes) { + public static boolean isCallOnAssertionContainer(ASTMethodCall call) { + return isCallOnType(call, ASSERT_CONTAINERS); + } + + private static boolean isCallOnType(ASTMethodCall call, Set qualifierTypes) { JTypeMirror declaring = call.getMethodType().getDeclaringType(); JTypeDeclSymbol sym = declaring.getSymbol(); String binaryName = !(sym instanceof JClassSymbol) ? null : ((JClassSymbol) sym).getBinaryName(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index e6961f3375..fba9edddb4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -278,6 +278,20 @@ public final class TypeTestUtil { /** * Matches a method or constructor call against a particular overload. + * Use {@link #parse(String, String)} to create one. For example, + * + *
+     *     java.lang.String#toString()   // match calls to toString on String instances
+     *     _#toString()                  // match calls to toString on any receiver
+     *     _#_()                         // match all calls to a method with no arguments
+     *     _#toString(_*)                // match calls to a "toString" method with any number of arguments
+     *     _#eq(_, _)                    // match calls to an "eq" method that has 2 parameters of unspecified type
+     *     _#eq(java.lang.String, _)     // like the previous, but the first parameter must be String
+     * 
+ * + *

The receiver parameter (first half) is matched against the + * static type of the receiver of the call (not the declaration site + * of the method). The parameters */ public static final class InvocationMatcher { @@ -345,14 +359,16 @@ public final class TypeTestUtil { /** - * Parses a {@link InvocationMatcher} from a string. + * Parses an {@link InvocationMatcher} from two strings. * - * @param qualifierMatcher Type matcher for the qualifier (either "_", or a qualified name). - * This will be matched with {@link #isA(String, TypeNode)} - * @param sig A signature in the form {@code name(arg1, arg2, ...)}, - * where each {@code argi} is either {@code _} or a qualified - * type name, without type arguments. - * These will be matched with {@link #isExactlyA(String, TypeNode)}. + *

The examples on the javadoc of this class are given in a + * syntax, where the first parameter of this method is before the #, + * and the second is after. + * + * @param qualifierMatcher Type matcher for the receiver type (either "_", or a qualified name). + * @param sig A signature in the form {@code name(arg1, arg2, ...)}, + * where each {@code argi} is either {@code _} or a qualified + * type name, without type arguments. * * @return A sig matcher * diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageTest.java index 93a3be36b1..ab7c80a5be 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class JUnitAssertionsShouldIncludeMessageTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml index 0bb995b977..7b49484f9c 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml @@ -8,8 +8,8 @@ assertArrayEquals ok 0 assertArrayEquals bad 1 0 assertThat bad 1 1 From b91d6dce04c9a800595542b21a0b0f54fb3a74da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 09:48:17 +0100 Subject: [PATCH 118/299] Finish porting junit rules --- .travis/all-java.xml | 4 +-- .../lang/java/ast/ASTAnyTypeDeclaration.java | 15 ++++++-- .../pmd/lang/java/rule/AbstractJUnitRule.java | 14 +++++++- .../rule/errorprone/JUnitStaticSuiteRule.java | 33 ++++++++++++------ .../TestClassWithoutTestCasesRule.java | 34 ++++++++----------- .../rule/errorprone/JUnitStaticSuiteTest.java | 1 - .../TestClassWithoutTestCasesTest.java | 1 - 7 files changed, 63 insertions(+), 39 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index 0fff277218..dc8933490b 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -234,7 +234,7 @@ - + @@ -258,7 +258,7 @@ - + diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java index 6fa413c51a..6c85470431 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnyTypeDeclaration.java @@ -157,14 +157,23 @@ public interface ASTAnyTypeDeclaration /** - * Retrieves the member declarations (fields, methods, classes, etc.) from the body of this type declaration. - * - * @return The member declarations declared in this type declaration + * Retrieves the member declarations (fields, methods, classes, etc.) + * from the body of this type declaration. */ default NodeStream getDeclarations() { return getBody().toStream(); } + /** + * Returns the declarations of a particular type. + * + * @param klass Type of the declarations + * @param Type of the declarations + */ + default NodeStream getDeclarations(Class klass) { + return getDeclarations().filterIs(klass); + } + /** * Returns the operations declared in this class (methods and constructors). diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java index 4b413b9652..1598a032c8 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java @@ -9,6 +9,7 @@ import java.util.List; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; @@ -81,7 +82,11 @@ public abstract class AbstractJUnitRule extends AbstractJavaRule { private static boolean isJUnit3Method(ASTMethodDeclaration method) { return TypeTestUtil.isA("junit.framework.TestCase", method.getEnclosingType()) - && method.isVoid() + && isJunit3MethodSignature(method); + } + + public static boolean isJunit3MethodSignature(ASTMethodDeclaration method) { + return method.isVoid() && method.isPublic() && method.getName().startsWith("test"); } @@ -91,6 +96,13 @@ public abstract class AbstractJUnitRule extends AbstractJavaRule { return TypeTestUtil.isA(JUNIT3_CLASS_NAME, cid); } + public static boolean isJUnit3Class(ASTAnyTypeDeclaration node) { + return node.isRegularClass() + && !node.isNested() + && !node.isAbstract() + && TypeTestUtil.isA(JUNIT3_CLASS_NAME, node); + } + private boolean isJUnit4Class(ASTCompilationUnit node) { return doesNodeContainJUnitAnnotation(node, JUNIT4_CLASS_NAME); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java index de0f71ada6..d12b7f81f5 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java @@ -5,20 +5,31 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; +import static net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule.isJUnit3Class; -public class JUnitStaticSuiteRule extends AbstractJUnitRule { +import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; + +public class JUnitStaticSuiteRule extends AbstractJavaRulechainRule { + + public JUnitStaticSuiteRule() { + super(ASTClassOrInterfaceDeclaration.class); + } @Override - public Object visit(ASTMethodDeclaration node, Object data) { - if (node.getArity() != 0) { - return super.visit(node, data); + public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { + if (isJUnit3Class(node)) { + ASTMethodDeclaration suiteMethod = node.getDeclarations() + .filterIs(ASTMethodDeclaration.class) + .filter(it -> "suite".equals(it.getName()) && it.getArity() == 0) + .first(); + if (suiteMethod != null + && (suiteMethod.getVisibility() != Visibility.V_PUBLIC || !suiteMethod.isStatic())) { + addViolation(data, suiteMethod); + } } - String name = node.getName(); - if ("suite".equals(name) && (!node.isStatic() || !node.isPublic())) { - addViolation(data, node); - } - return super.visit(node, data); + return null; } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java index be3f2a82ba..5786ba47b2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java @@ -4,36 +4,30 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import java.util.List; +import static net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule.isJUnit3Class; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -public class TestClassWithoutTestCasesRule extends AbstractJUnitRule { +public class TestClassWithoutTestCasesRule extends AbstractJavaRulechainRule { + + public TestClassWithoutTestCasesRule() { + super(ASTClassOrInterfaceDeclaration.class); + } @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - if (node.isAbstract() || node.isInterface() || node.isNested()) { - return data; - } + if (isJUnit3Class(node)) { + boolean hasTests = + node.getDeclarations(ASTMethodDeclaration.class) + .any(AbstractJUnitRule::isJunit3MethodSignature); - List m = node.findDescendantsOfType(ASTMethodDeclaration.class); - boolean testsFound = false; - - if (m != null) { - for (ASTMethodDeclaration md : m) { - if (isJUnitMethod(md)) { - testsFound = true; - break; - } + if (!hasTests) { + addViolation(data, node); } } - - if (!testsFound) { - addViolation(data, node); - } - - return data; + return null; } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteTest.java index c4e17f7a50..67b9a62bb5 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class JUnitStaticSuiteTest extends PmdRuleTst { // no additional unit tests } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesTest.java index cff9c119fd..c901a75521 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class TestClassWithoutTestCasesTest extends PmdRuleTst { // no additional unit tests } From ee8f1ef2f975cc98fa1c1e96e9e862b94a06753a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 09:54:04 +0100 Subject: [PATCH 119/299] Delete AbstractJunitRule --- .../pmd/lang/java/rule/AbstractJUnitRule.java | 141 ------------------ ...nitAssertionsShouldIncludeMessageRule.java | 3 +- .../JUnitTestsShouldIncludeAssertRule.java | 41 +---- .../bestpractices/JUnitUseExpectedRule.java | 2 +- .../rule/errorprone/JUnitSpellingRule.java | 9 +- .../rule/errorprone/JUnitStaticSuiteRule.java | 2 +- .../TestClassWithoutTestCasesRule.java | 6 +- .../java/rule/internal/JUnitRuleUtil.java | 111 ++++++++++++++ 8 files changed, 125 insertions(+), 190 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java deleted file mode 100644 index 1598a032c8..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/AbstractJUnitRule.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule; - -import java.util.List; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; -import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; -import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.TypeNode; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; - -/** - * @deprecated Internal API - */ -@Deprecated -@InternalApi -public abstract class AbstractJUnitRule extends AbstractJavaRule { - - public static final String JUNIT3_CLASS_NAME = "junit.framework.TestCase"; - public static final String JUNIT4_CLASS_NAME = "org.junit.Test"; - public static final String JUNIT5_CLASS_NAME = "org.junit.jupiter.api.Test"; - - protected boolean isJUnit3Class; - protected boolean isJUnit4Class; - protected boolean isJUnit5Class; - - @Override - public Object visit(ASTCompilationUnit node, Object data) { - - isJUnit3Class = false; - isJUnit4Class = false; - isJUnit5Class = false; - - isJUnit3Class = isJUnit3Class(node); - isJUnit4Class = isJUnit4Class(node); - isJUnit5Class = isJUnit5Class(node); - - if (isJUnit4Class && isJUnit5Class) { - isJUnit4Class &= hasImports(node, JUNIT4_CLASS_NAME); - isJUnit5Class &= hasImports(node, JUNIT5_CLASS_NAME); - } - - if (!isTestNgClass(node) && (isJUnit3Class || isJUnit4Class || isJUnit5Class)) { - return super.visit(node, data); - } - return data; - } - - private boolean isTestNgClass(ASTCompilationUnit node) { - return node.children(ASTImportDeclaration.class) - .any(i -> i.getImportedName().startsWith("org.testng")); - } - - public static boolean isJUnitMethod(ASTMethodDeclaration method) { - if (method.isStatic() || method.getBody() == null) { - return false; // skip various inapplicable method variations - } - - boolean result = false; - result = result || isJUnit5Method(method); - result = result || isJUnit4Method(method); - result = result || isJUnit3Method(method); - return result; - } - - private static boolean isJUnit4Method(ASTMethodDeclaration method) { - return method.isAnnotationPresent(JUNIT4_CLASS_NAME) && method.isPublic(); - } - - private static boolean isJUnit5Method(ASTMethodDeclaration method) { - return method.isAnnotationPresent(JUNIT5_CLASS_NAME); - } - - private static boolean isJUnit3Method(ASTMethodDeclaration method) { - return TypeTestUtil.isA("junit.framework.TestCase", method.getEnclosingType()) - && isJunit3MethodSignature(method); - } - - public static boolean isJunit3MethodSignature(ASTMethodDeclaration method) { - return method.isVoid() - && method.isPublic() - && method.getName().startsWith("test"); - } - - private boolean isJUnit3Class(ASTCompilationUnit node) { - ASTClassOrInterfaceDeclaration cid = node.getFirstDescendantOfType(ASTClassOrInterfaceDeclaration.class); - return TypeTestUtil.isA(JUNIT3_CLASS_NAME, cid); - } - - public static boolean isJUnit3Class(ASTAnyTypeDeclaration node) { - return node.isRegularClass() - && !node.isNested() - && !node.isAbstract() - && TypeTestUtil.isA(JUNIT3_CLASS_NAME, node); - } - - private boolean isJUnit4Class(ASTCompilationUnit node) { - return doesNodeContainJUnitAnnotation(node, JUNIT4_CLASS_NAME); - } - - private boolean isJUnit5Class(ASTCompilationUnit node) { - return doesNodeContainJUnitAnnotation(node, JUNIT5_CLASS_NAME); - } - - private boolean doesNodeContainJUnitAnnotation(Node node, String annotationTypeClassName) { - List annotations = node.findDescendantsOfType(ASTAnnotation.class); - for (ASTAnnotation annotation : annotations) { - Node annotationTypeNode = annotation.getChild(0); - TypeNode annotationType = (TypeNode) annotationTypeNode; - if (annotationType.getType() == null) { - ASTName name = annotationTypeNode.getFirstChildOfType(ASTName.class); - if (name != null && (name.hasImageEqualTo("Test") || name.hasImageEqualTo(annotationTypeClassName))) { - return true; - } - } else if (TypeTestUtil.isA(annotationTypeClassName, annotationType)) { - return true; - } - } - return false; - } - - private boolean hasImports(ASTCompilationUnit cu, String className) { - List imports = cu.findDescendantsOfType(ASTImportDeclaration.class); - for (ASTImportDeclaration importDeclaration : imports) { - ASTName name = importDeclaration.getFirstChildOfType(ASTName.class); - if (name != null && name.hasImageEqualTo(className)) { - return true; - } - } - return false; - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java index c904f758c7..445284b222 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java @@ -10,6 +10,7 @@ import java.util.List; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil; import net.sourceforge.pmd.lang.java.types.TypeTestUtil.InvocationMatcher; public class JUnitAssertionsShouldIncludeMessageRule extends AbstractJavaRulechainRule { @@ -38,7 +39,7 @@ public class JUnitAssertionsShouldIncludeMessageRule extends AbstractJavaRulecha @Override public Object visit(ASTMethodCall node, Object data) { - if (JUnitTestsShouldIncludeAssertRule.isCallOnAssertionContainer(node)) { + if (JUnitRuleUtil.isAssertCall(node)) { for (InvocationMatcher check : checks) { if (check.matchesCall(node)) { addViolation(data, node); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index ed3b9cbd74..fde508ff37 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -4,31 +4,16 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import static net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule.JUNIT4_CLASS_NAME; -import static net.sourceforge.pmd.util.CollectionUtil.setOf; - -import java.util.Set; - import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; -import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; -import net.sourceforge.pmd.lang.java.types.JTypeMirror; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil; public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule { - private static final Set MOCKITO = setOf("org.mockito.Mockito"); - private static final Set ASSERT_CONTAINERS = setOf("org.junit.Assert", - "org.junit.jupiter.api.Assertions", - "org.hamcrest.MatcherAssert", - "junit.framework.TestCase"); - public JUnitTestsShouldIncludeAssertRule() { super(ASTMethodDeclaration.class); } @@ -37,9 +22,9 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule public Object visit(ASTMethodDeclaration method, Object data) { ASTBlock body = method.getBody(); if (body != null - && AbstractJUnitRule.isJUnitMethod(method) + && JUnitRuleUtil.isJUnitMethod(method) && !isExpectAnnotated(method) - && body.descendants(ASTMethodCall.class).none(JUnitTestsShouldIncludeAssertRule::isAssertCall)) { + && body.descendants(ASTMethodCall.class).none(JUnitRuleUtil::isAssertCall)) { addViolation(data, method); } return data; @@ -50,28 +35,10 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule */ private boolean isExpectAnnotated(ASTMethodDeclaration method) { return method.getDeclaredAnnotations() - .filter(it -> TypeTestUtil.isA(JUNIT4_CLASS_NAME, it)) + .filter(JUnitRuleUtil::isJunit4TestAnnotation) .flatMap(ASTAnnotation::getMembers) .any(it -> "expected".equals(it.getName())); } - private static boolean isAssertCall(ASTMethodCall call) { - String name = call.getMethodName(); - return name.equals("expect") && TypeTestUtil.isA("org.junit.rules.ExpectedException", call.getQualifier()) - || name.equals("assertAll") && TypeTestUtil.isA("org.assertj.core.api.SoftAssertions", call.getQualifier()) - || name.equals("verify") && isCallOnType(call, MOCKITO) - || (name.startsWith("assert") || name.equals("fail")) && isCallOnAssertionContainer(call); - } - - public static boolean isCallOnAssertionContainer(ASTMethodCall call) { - return isCallOnType(call, ASSERT_CONTAINERS); - } - - private static boolean isCallOnType(ASTMethodCall call, Set qualifierTypes) { - JTypeMirror declaring = call.getMethodType().getDeclaringType(); - JTypeDeclSymbol sym = declaring.getSymbol(); - String binaryName = !(sym instanceof JClassSymbol) ? null : ((JClassSymbol) sym).getBinaryName(); - return qualifierTypes.contains(binaryName); - } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java index f82529d4b2..d3d5209840 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import static net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule.isJUnitMethod; +import static net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil.isJUnitMethod; import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java index 5944d0a237..446449f9c5 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java @@ -5,12 +5,10 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import static net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule.JUNIT3_CLASS_NAME; - import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil; public class JUnitSpellingRule extends AbstractJavaRulechainRule { @@ -20,9 +18,8 @@ public class JUnitSpellingRule extends AbstractJavaRulechainRule { @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - if (node.isRegularClass() && TypeTestUtil.isA(JUNIT3_CLASS_NAME, node)) { - node.getDeclarations() - .filterIs(ASTMethodDeclaration.class) + if (JUnitRuleUtil.isJUnit3Class(node)) { + node.getDeclarations(ASTMethodDeclaration.class) .filter(this::isViolation) .forEach(it -> addViolation(data, it)); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java index d12b7f81f5..ce3f590a24 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java @@ -5,7 +5,7 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import static net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule.isJUnit3Class; +import static net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil.isJUnit3Class; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java index 5786ba47b2..b06c978421 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java @@ -4,12 +4,12 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import static net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule.isJUnit3Class; +import static net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil.isJUnit3Class; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil; public class TestClassWithoutTestCasesRule extends AbstractJavaRulechainRule { @@ -22,7 +22,7 @@ public class TestClassWithoutTestCasesRule extends AbstractJavaRulechainRule { if (isJUnit3Class(node)) { boolean hasTests = node.getDeclarations(ASTMethodDeclaration.class) - .any(AbstractJUnitRule::isJunit3MethodSignature); + .any(JUnitRuleUtil::isJunit3MethodSignature); if (!hasTests) { addViolation(data, node); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java new file mode 100644 index 0000000000..bb816bf00e --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java @@ -0,0 +1,111 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.internal; + +import static net.sourceforge.pmd.util.CollectionUtil.setOf; + +import java.util.Set; + +import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; +import net.sourceforge.pmd.lang.java.types.JTypeMirror; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; + +/** + * Utilities for junit-related rules. + */ +public final class JUnitRuleUtil { + + private static final String JUNIT3_CLASS_NAME = "junit.framework.TestCase"; + private static final String JUNIT4_TEST_ANNOT = "org.junit.Test"; + private static final String JUNIT5_TEST_ANNOT = "org.junit.jupiter.api.Test"; + private static final Set MOCKITO = setOf("org.mockito.Mockito"); + private static final Set ASSERT_CONTAINERS = setOf("org.junit.Assert", + "org.junit.jupiter.api.Assertions", + "org.hamcrest.MatcherAssert", + "junit.framework.TestCase"); + + private JUnitRuleUtil() { + // utility class + } + + /** + * True if this is a junit @Test method (or a junit 3 method). + */ + public static boolean isJUnitMethod(ASTMethodDeclaration method) { + if (method.isStatic() || method.getBody() == null) { + return false; // skip various inapplicable method variations + } + + boolean result = false; + result = result || isJUnit5Method(method); + result = result || isJUnit4Method(method); + result = result || isJUnit3Method(method); + return result; + } + + private static boolean isJUnit4Method(ASTMethodDeclaration method) { + return method.isAnnotationPresent(JUNIT4_TEST_ANNOT) && method.isPublic(); + } + + private static boolean isJUnit5Method(ASTMethodDeclaration method) { + return method.isAnnotationPresent(JUNIT5_TEST_ANNOT); + } + + private static boolean isJUnit3Method(ASTMethodDeclaration method) { + return TypeTestUtil.isA("junit.framework.TestCase", method.getEnclosingType()) + && isJunit3MethodSignature(method); + } + + public static boolean isJunit4TestAnnotation(ASTAnnotation annot) { + return TypeTestUtil.isA(JUNIT4_TEST_ANNOT, annot); + } + + /** + * Does not check the class (use {@link #isJUnit3Class(ASTAnyTypeDeclaration)}). + */ + public static boolean isJunit3MethodSignature(ASTMethodDeclaration method) { + return method.isVoid() + && method.isPublic() + && method.getName().startsWith("test"); + } + + /** + * True if this is a {@code TestCase} class for Junit 3. + */ + public static boolean isJUnit3Class(ASTAnyTypeDeclaration node) { + return node.isRegularClass() + && !node.isNested() + && !node.isAbstract() + && TypeTestUtil.isA(JUNIT3_CLASS_NAME, node); + } + + /** + * True if this is a call to an assert/fail method. Supports a lot + * of different patterns. + */ + public static boolean isAssertCall(ASTMethodCall call) { + String name = call.getMethodName(); + return name.equals("expect") && TypeTestUtil.isA("org.junit.rules.ExpectedException", call.getQualifier()) + || name.equals("assertAll") && TypeTestUtil.isA("org.assertj.core.api.SoftAssertions", call.getQualifier()) + || name.equals("verify") && isCallOnType(call, MOCKITO) + || (name.startsWith("assert") || name.equals("fail")) && isCallOnAssertionContainer(call); + } + + private static boolean isCallOnAssertionContainer(ASTMethodCall call) { + return isCallOnType(call, ASSERT_CONTAINERS); + } + + private static boolean isCallOnType(ASTMethodCall call, Set qualifierTypes) { + JTypeMirror declaring = call.getMethodType().getDeclaringType(); + JTypeDeclSymbol sym = declaring.getSymbol(); + String binaryName = !(sym instanceof JClassSymbol) ? null : ((JClassSymbol) sym).getBinaryName(); + return qualifierTypes.contains(binaryName); + } +} From 8941a2ef7f737594da154c8fce48a412f8dce955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 09:59:50 +0100 Subject: [PATCH 120/299] Simplify code --- .../java/multifile/signature/JavaOperationSignature.java | 3 +-- .../lang/java/rule/bestpractices/MissingOverrideRule.java | 2 +- .../java/rule/codestyle/UnnecessaryConstructorRule.java | 2 +- .../pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java | 3 +-- .../java/symbols/table/internal/SymbolTableResolver.java | 6 ++---- 5 files changed, 6 insertions(+), 10 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaOperationSignature.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaOperationSignature.java index 82ea70a122..22995d539d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaOperationSignature.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaOperationSignature.java @@ -109,8 +109,7 @@ public final class JavaOperationSignature extends JavaSignature fieldNames = node.getEnclosingType() - .getDeclarations() - .filterIs(ASTFieldDeclaration.class) + .getDeclarations(ASTFieldDeclaration.class) .flatMap(ASTFieldDeclaration::getVarIds) .collect(Collectors.toMap( f -> { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java index 2ba5f6ff58..a7f3dbef26 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/MissingOverrideRule.java @@ -55,7 +55,7 @@ public class MissingOverrideRule extends AbstractJavaRule { RelevantMethodSet relevantMethods = new RelevantMethodSet(node.getSymbol()); - for (ASTMethodDeclaration methodDecl : node.getDeclarations().filterIs(ASTMethodDeclaration.class)) { + for (ASTMethodDeclaration methodDecl : node.getDeclarations(ASTMethodDeclaration.class)) { relevantMethods.addIfRelevant(methodDecl); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorRule.java index 5f9df5df58..f3e9e4a253 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/UnnecessaryConstructorRule.java @@ -53,7 +53,7 @@ public class UnnecessaryConstructorRule extends AbstractIgnoredAnnotationRule { } private void checkClassOrEnum(ASTAnyTypeDeclaration node, Object data) { - List ctors = node.getDeclarations().filterIs(ASTConstructorDeclaration.class).take(2).toList(); + List ctors = node.getDeclarations(ASTConstructorDeclaration.class).take(2).toList(); if (ctors.size() == 1 && isExplicitDefaultConstructor(node, ctors.get(0))) { addViolation(data, ctors.get(0)); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java index ce3f590a24..a013557e03 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java @@ -21,8 +21,7 @@ public class JUnitStaticSuiteRule extends AbstractJavaRulechainRule { @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { if (isJUnit3Class(node)) { - ASTMethodDeclaration suiteMethod = node.getDeclarations() - .filterIs(ASTMethodDeclaration.class) + ASTMethodDeclaration suiteMethod = node.getDeclarations(ASTMethodDeclaration.class) .filter(it -> "suite".equals(it.getName()) && it.getArity() == 0) .first(); if (suiteMethod != null diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java index 7273b2098a..8203afd67b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java @@ -245,14 +245,12 @@ public final class SymbolTableResolver { setTopSymbolTable(node.getBody()); // preprocess siblings - node.getDeclarations() - .filterIs(ASTAnyTypeDeclaration.class) + node.getDeclarations(ASTAnyTypeDeclaration.class) .forEach(d -> processTypeHeader(d, bodyCtx)); // process fields first, their type is needed for JSymbolTable#resolveValue - f.disambig(node.getDeclarations() - .filterIs(ASTFieldDeclaration.class) + f.disambig(node.getDeclarations(ASTFieldDeclaration.class) .map(ASTFieldDeclaration::getTypeNode), bodyCtx); visitChildren(node.getBody(), bodyCtx); From 6aceb3d5e23595422c2abf0c07d404469278cf08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 10:41:50 +0100 Subject: [PATCH 121/299] Fix pmd warnings --- .../java/rule/bestpractices/JUnitUseExpectedRule.java | 2 +- .../pmd/lang/java/rule/internal/JUnitRuleUtil.java | 8 ++++---- .../net/sourceforge/pmd/lang/java/types/TypeTestUtil.java | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java index d3d5209840..363e6ff02c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java @@ -64,7 +64,7 @@ public class JUnitUseExpectedRule extends AbstractJavaRulechainRule { private boolean isFailStmt(ASTExpressionStatement stmt) { if (stmt.getExpr() instanceof ASTMethodCall) { ASTMethodCall expr = (ASTMethodCall) stmt.getExpr(); - return expr.getMethodName().equals("fail"); + return "fail".equals(expr.getMethodName()); } return false; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java index bb816bf00e..8d68f1060a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java @@ -92,10 +92,10 @@ public final class JUnitRuleUtil { */ public static boolean isAssertCall(ASTMethodCall call) { String name = call.getMethodName(); - return name.equals("expect") && TypeTestUtil.isA("org.junit.rules.ExpectedException", call.getQualifier()) - || name.equals("assertAll") && TypeTestUtil.isA("org.assertj.core.api.SoftAssertions", call.getQualifier()) - || name.equals("verify") && isCallOnType(call, MOCKITO) - || (name.startsWith("assert") || name.equals("fail")) && isCallOnAssertionContainer(call); + return "expect".equals(name) && TypeTestUtil.isA("org.junit.rules.ExpectedException", call.getQualifier()) + || "assertAll".equals(name) && TypeTestUtil.isA("org.assertj.core.api.SoftAssertions", call.getQualifier()) + || "verify".equals(name) && isCallOnType(call, MOCKITO) + || (name.startsWith("assert") || "fail".equals(name)) && isCallOnAssertionContainer(call); } private static boolean isCallOnAssertionContainer(ASTMethodCall call) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index fba9edddb4..9d33c7b59e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -151,7 +151,7 @@ public final class TypeTestUtil { || otherType.isClassOrInterface() && ((JClassType) otherType).getSymbol().isAnonymousClass()) { return false; // we know isExactlyA(canonicalName, node); returned false } else if (otherType.isPrimitive()) { - return otherType == thisType; // isSubtypeOf considers primitive widening like subtyping + return otherType.equals(thisType); // isSubtypeOf considers primitive widening like subtyping } return thisType.isSubtypeOf(otherType); @@ -300,7 +300,7 @@ public final class TypeTestUtil { final TypeMatcher qualifierMatcher; InvocationMatcher(TypeMatcher qualifierMatcher, String expectedName, @Nullable List argMatchers) { - this.expectedName = expectedName.equals("_") ? null : expectedName; + this.expectedName = "_".equals(expectedName) ? null : expectedName; this.argMatchers = argMatchers; this.qualifierMatcher = qualifierMatcher; } From 8b63dae494e9c6f328c97add0f7bb38f63ee580a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 16:29:04 +0100 Subject: [PATCH 122/299] Review --- .../AbstractNamingConventionRule.java | 9 ++++++-- .../codestyle/ClassNamingConventionsRule.java | 21 ++++++++----------- .../codestyle/FieldNamingConventionsRule.java | 9 +------- .../FormalParameterNamingConventionsRule.java | 11 +--------- .../LocalVariableNamingConventionsRule.java | 2 ++ .../MethodNamingConventionsRule.java | 9 +------- .../AppendCharacterWithCharRule.java | 15 ++++++------- .../resources/category/java/errorprone.xml | 4 ++-- 8 files changed, 29 insertions(+), 51 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java index 7d8525a0db..6027e2ebea 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/AbstractNamingConventionRule.java @@ -7,7 +7,7 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import java.util.regex.Pattern; import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.properties.PropertyBuilder.RegexPropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; @@ -24,11 +24,16 @@ import net.sourceforge.pmd.util.StringUtil.CaseConvention; * @author Clément Fournier * @since 6.5.0 */ -abstract class AbstractNamingConventionRule extends AbstractJavaRule { +abstract class AbstractNamingConventionRule extends AbstractJavaRulechainRule { static final String CAMEL_CASE = "[a-z][a-zA-Z0-9]*"; static final String PASCAL_CASE = "[A-Z][a-zA-Z0-9]*"; + @SafeVarargs + protected AbstractNamingConventionRule(Class first, Class... visits) { + super(first, visits); + } + /** The argument is interpreted as the display name, and is converted to camel case to get the property name. */ RegexPropertyBuilder defaultProp(String displayName) { return defaultProp(CaseConvention.SPACE_SEPARATED.convertTo(CaseConvention.CAMEL_CASE, displayName), displayName); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java index db143dc85f..8055d987f4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java @@ -6,8 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; import java.util.regex.Pattern; -import org.checkerframework.checker.nullness.qual.NonNull; - import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; @@ -15,7 +13,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTRecordDeclaration; import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil; import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil; -import net.sourceforge.pmd.lang.rule.RuleTargetSelector; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -33,6 +30,10 @@ public class ClassNamingConventionsRule extends AbstractNamingConventionRulefeature request #381 Single character StringBuffer.append */ -public class AppendCharacterWithCharRule extends AbstractJavaRule { +public class AppendCharacterWithCharRule extends AbstractJavaRulechainRule { - @Override - protected @NonNull RuleTargetSelector buildTargetSelector() { - return RuleTargetSelector.forTypes(ASTStringLiteral.class); + public AppendCharacterWithCharRule() { + super(ASTStringLiteral.class); } @Override public Object visit(ASTStringLiteral node, Object data) { - if (node.length() == 1 && node.getParent() instanceof ASTArgumentList + if (node.getParent() instanceof ASTArgumentList + && node.length() == 1 && ((ASTArgumentList) node.getParent()).size() == 1) { JavaNode callParent = node.getParent().getParent(); if (callParent instanceof ASTMethodCall) { diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index c8c1ae08f0..1437d3e1f6 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1663,8 +1663,8 @@ and should be removed. Date: Tue, 3 Nov 2020 17:22:11 +0100 Subject: [PATCH 123/299] Review --- .../resources/category/java/bestpractices.xml | 2 +- .../resources/category/java/errorprone.xml | 1 + .../xml/JUnitTestContainsTooManyAsserts.xml | 18 ++++++++++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 733945ad36..e3e00a93a3 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -819,7 +819,7 @@ This rule checks for JUnit4, JUnit5 and TestNG Tests, as well as methods startin or pmd-java:hasAnnotation('org.junit.jupiter.params.ParameterizedTest') or pmd-java:hasAnnotation('org.testng.annotations.Test') ] - [Block[count(//MethodCall[@MethodName[matches(.,'^assert')]]) > $maximumAsserts]] + [Block[count(.//MethodCall[@MethodName[matches(.,'^assert')]]) > $maximumAsserts]] ]]> diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 1437d3e1f6..b3e5ecace8 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1665,6 +1665,7 @@ and should be removed. [not( parent::WhileStatement or parent::ForStatement + or parent::ForeachStatement or preceding-sibling::*[1]/self::LocalClassStatement ) ] diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestContainsTooManyAsserts.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestContainsTooManyAsserts.xml index eb7ed6d988..30cb51c20a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestContainsTooManyAsserts.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestContainsTooManyAsserts.xml @@ -199,6 +199,24 @@ public class MyTestCase { assertFalse("myVar should be false",myVar); assertEquals("should equals false", false, myVar); } +} + ]]> + + + Should not count unrelated asserts + 0 + From 21f26b75ab0d32ee5697e688a94784f5d2a53630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 17:22:36 +0100 Subject: [PATCH 124/299] Change UseConcurrentHashMap --- .../category/java/multithreading.xml | 10 +++------ .../xml/UseConcurrentHashMap.xml | 22 +++++++++++++++---- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index b45eb8db91..cef64e0ba7 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -338,7 +338,7 @@ perform efficient map reads without blocking other threads. @@ -346,13 +346,9 @@ perform efficient map reads without blocking other threads. map1 = Collections.synchronizedMap(new HashMap<>()); } ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml index c603a04286..58ab412ddb 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml @@ -7,17 +7,18 @@ Basic use case 2 + 3,6 map1 = Collections.synchronizedMap(new HashMap<>()); public void m() { - Map m = new HashMap(); + Map map1 = Collections.synchronizedMap(new TreeMap<>()); } public Map contructMap() { - return new HashMap(); //not detected + return new HashMap(); //not flagged } } ]]> @@ -47,9 +48,22 @@ import java.util.*; import java.util.concurrent.*; public class ConcurrentApp { public void getMyInstance() { - Map map1 = new HashMap(); // fine for single-threaded access --- violation on this line + Map map1 = Collections.synchronizedMap(new HashMap<>()); // violation on this line Map map2 = new ConcurrentHashMap(); // preferred for use with multiple threads } +} + ]]> + + + FP with Properties + 0 + From 384dff44d9ee4b14ad498dd633dfd5b88e20ec31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 3 Nov 2020 17:50:53 +0100 Subject: [PATCH 125/299] Fixes for regression test --- ...nitAssertionsShouldIncludeMessageRule.java | 2 +- .../JUnitTestsShouldIncludeAssertRule.java | 13 ++++++++- .../java/rule/internal/JUnitRuleUtil.java | 15 ++-------- .../resources/category/java/codestyle.xml | 4 +-- .../resources/category/java/documentation.xml | 4 +-- .../resources/category/java/errorprone.xml | 6 ++-- .../xml/UncommentedEmptyConstructor.xml | 1 + .../errorprone/xml/EmptyStatementBlock.xml | 28 +++++++++++++++++++ 8 files changed, 52 insertions(+), 21 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java index 445284b222..0ef169c1f7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java @@ -39,7 +39,7 @@ public class JUnitAssertionsShouldIncludeMessageRule extends AbstractJavaRulecha @Override public Object visit(ASTMethodCall node, Object data) { - if (JUnitRuleUtil.isAssertCall(node)) { + if (JUnitRuleUtil.isCallOnAssertionContainer(node)) { for (InvocationMatcher check : checks) { if (check.matchesCall(node)) { addViolation(data, node); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index fde508ff37..394ba95b3b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -24,12 +24,23 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule if (body != null && JUnitRuleUtil.isJUnitMethod(method) && !isExpectAnnotated(method) - && body.descendants(ASTMethodCall.class).none(JUnitRuleUtil::isAssertCall)) { + && body.descendants(ASTMethodCall.class) + .none(JUnitTestsShouldIncludeAssertRule::isProbableAssertCall)) { addViolation(data, method); } return data; } + private static boolean isProbableAssertCall(ASTMethodCall call) { + String name = call.getMethodName(); + return name.startsWith("assert") + || name.startsWith("check") + || name.startsWith("verify") + || name.equals("fail") + || name.equals("failWith") + || JUnitRuleUtil.isExpectExceptionCall(call); + } + /** * Tells if the node contains a Test annotation with an expected exception. */ diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java index 8d68f1060a..0412d5e286 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java @@ -25,7 +25,6 @@ public final class JUnitRuleUtil { private static final String JUNIT3_CLASS_NAME = "junit.framework.TestCase"; private static final String JUNIT4_TEST_ANNOT = "org.junit.Test"; private static final String JUNIT5_TEST_ANNOT = "org.junit.jupiter.api.Test"; - private static final Set MOCKITO = setOf("org.mockito.Mockito"); private static final Set ASSERT_CONTAINERS = setOf("org.junit.Assert", "org.junit.jupiter.api.Assertions", "org.hamcrest.MatcherAssert", @@ -86,19 +85,11 @@ public final class JUnitRuleUtil { && TypeTestUtil.isA(JUNIT3_CLASS_NAME, node); } - /** - * True if this is a call to an assert/fail method. Supports a lot - * of different patterns. - */ - public static boolean isAssertCall(ASTMethodCall call) { - String name = call.getMethodName(); - return "expect".equals(name) && TypeTestUtil.isA("org.junit.rules.ExpectedException", call.getQualifier()) - || "assertAll".equals(name) && TypeTestUtil.isA("org.assertj.core.api.SoftAssertions", call.getQualifier()) - || "verify".equals(name) && isCallOnType(call, MOCKITO) - || (name.startsWith("assert") || "fail".equals(name)) && isCallOnAssertionContainer(call); + public static boolean isExpectExceptionCall(ASTMethodCall call) { + return "expect".equals(call.getMethodName()) && TypeTestUtil.isA("org.junit.rules.ExpectedException", call.getQualifier()); } - private static boolean isCallOnAssertionContainer(ASTMethodCall call) { + public static boolean isCallOnAssertionContainer(ASTMethodCall call) { return isCallOnType(call, ASSERT_CONTAINERS); } diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index c89f53d28e..7715918ad2 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -513,11 +513,11 @@ usage by developers who should be implementing their own versions in the concret //ClassOrInterfaceDeclaration[@RegularClass = true() and pmd-java:modifiers() = "abstract"] /ClassOrInterfaceBody /MethodDeclaration - /Block[ + [Block[ let $size := count(*[not(self::EmptyStatement)]) return $size = 0 or $size = 1 and ReturnStatement[ NullLiteral or NumericLiteral[@ValueAsInt = 0] or StringLiteral[@Empty = true()]] - ] + ]] ]]> diff --git a/pmd-java/src/main/resources/category/java/documentation.xml b/pmd-java/src/main/resources/category/java/documentation.xml index babf7d5784..cd74404fa7 100644 --- a/pmd-java/src/main/resources/category/java/documentation.xml +++ b/pmd-java/src/main/resources/category/java/documentation.xml @@ -98,10 +98,10 @@ and unintentional empty constructors. diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index b3e5ecace8..2edd8b2125 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1568,7 +1568,7 @@ Empty If Statement finds instances where a condition is checked but nothing is d @@ -1626,7 +1626,7 @@ Empty block statements serve no purpose and should be removed. 3 - //Block/Block[not(*)] + //Block[not(*)][parent::Block or parent::SwitchFallthroughBranch or parent::SwitchArrowBranch] @@ -1790,7 +1790,7 @@ a while loop that does a lot in the exit expression, rewrite it to make it clear diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/documentation/xml/UncommentedEmptyConstructor.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/documentation/xml/UncommentedEmptyConstructor.xml index 2d11209715..fee76365db 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/documentation/xml/UncommentedEmptyConstructor.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/documentation/xml/UncommentedEmptyConstructor.xml @@ -8,6 +8,7 @@ simple failure true 1 + 2 + + + block in switch case + 1 + 4 + + + + block in switch case (arrow) + 1 + 4 + { } + } + } } ]]> From fe3c8c36b40dc7331429b2831085fba3025202e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 4 Nov 2020 05:54:28 +0100 Subject: [PATCH 126/299] Revert "Change UseConcurrentHashMap" We can do that later This reverts commit 21f26b75ab0d32ee5697e688a94784f5d2a53630. --- .../resources/category/java/multithreading.xml | 10 +++++++--- .../xml/UseConcurrentHashMap.xml | 18 +++++++++--------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index cef64e0ba7..74b8093c20 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -338,7 +338,7 @@ perform efficient map reads without blocking other threads. @@ -346,9 +346,13 @@ perform efficient map reads without blocking other threads. map1 = Collections.synchronizedMap(new HashMap<>()); + // the following case will be ignored by this rule + Map map3 = someModule.methodThatReturnMap(); // might be OK, if the returned map is already thread-safe + } } ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml index 58ab412ddb..c093dce805 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseConcurrentHashMap.xml @@ -7,18 +7,17 @@ Basic use case 2 - 3,6 map1 = Collections.synchronizedMap(new HashMap<>()); + private Map _map = new TreeMap(); public void m() { - Map map1 = Collections.synchronizedMap(new TreeMap<>()); + Map m = new HashMap(); } public Map contructMap() { - return new HashMap(); //not flagged + return new HashMap(); //not detected } } ]]> @@ -48,7 +47,7 @@ import java.util.*; import java.util.concurrent.*; public class ConcurrentApp { public void getMyInstance() { - Map map1 = Collections.synchronizedMap(new HashMap<>()); // violation on this line + Map map1 = new HashMap(); // fine for single-threaded access --- violation on this line Map map2 = new ConcurrentHashMap(); // preferred for use with multiple threads } } @@ -58,13 +57,14 @@ public class ConcurrentApp { FP with Properties 0 - + } + ]]> + + From 658fa3fee3fd75de02829e0eaf597b81b4e66c16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 4 Nov 2020 06:56:05 +0100 Subject: [PATCH 127/299] Improve CompareObjectsWithEquals --- .../CompareObjectsWithEqualsRule.java | 34 ++++++++++++++++++- .../xml/CompareObjectsWithEquals.xml | 20 +++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java index 750ffbc866..29cd781809 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java @@ -4,10 +4,15 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; +import org.checkerframework.checker.nullness.qual.NonNull; + import net.sourceforge.pmd.lang.java.ast.ASTArrayAllocation; +import net.sourceforge.pmd.lang.java.ast.ASTBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTThisExpression; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; @@ -37,7 +42,7 @@ public class CompareObjectsWithEqualsRule extends AbstractJavaRulechainRule { return data; } - if (!isIgnoredType(left) && !isIgnoredType(right)) { + if (!isAllowedIdentityComparison(node)) { addViolation(data, node); } @@ -50,4 +55,31 @@ public class CompareObjectsWithEqualsRule extends AbstractJavaRulechainRule { || TypeTestUtil.isA(Class.class, left); } + private boolean isAllowedIdentityComparison(ASTInfixExpression infix) { + ASTBodyDeclaration enclosing = infix.ancestors(ASTBodyDeclaration.class).first(); + + ASTExpression left = infix.getLeftOperand(); + ASTExpression right = infix.getRightOperand(); + if (isEqualsMethod(enclosing) && (isThisExpr(left) || isThisExpr(right))) { + // this == _ is allowed in equals methods + return true; + } else { + return isIgnoredType(left) || isIgnoredType(right); + } + } + + private static boolean isThisExpr(@NonNull ASTExpression expr) { + return expr instanceof ASTThisExpression; + } + + private static boolean isEqualsMethod(ASTBodyDeclaration first) { + if (first instanceof ASTMethodDeclaration) { + ASTMethodDeclaration m = (ASTMethodDeclaration) first; + return "equals".equals(m.getName()) + && m.getArity() == 1 + && TypeTestUtil.isExactlyA(Object.class, m.getFormalParameters().get(0)); + } + return false; + } + } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index 632de9db3c..44138dc29d 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -207,6 +207,26 @@ public class CompareWithEqualsTest { { return a == b; } +} + ]]> + + + comparing in equals method should be ok + 0 + From 93062200cd22d58c9a075cc3d8e75442a98f3980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 4 Nov 2020 07:08:50 +0100 Subject: [PATCH 128/299] Ignore Thread.currentThread().getContextClassLoader() --- .../src/main/resources/category/java/multithreading.xml | 9 ++++++++- .../java/rule/multithreading/xml/DoNotUseThreads.xml | 9 +++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/pmd-java/src/main/resources/category/java/multithreading.xml b/pmd-java/src/main/resources/category/java/multithreading.xml index 74b8093c20..ad13849a9c 100644 --- a/pmd-java/src/main/resources/category/java/multithreading.xml +++ b/pmd-java/src/main/resources/category/java/multithreading.xml @@ -150,7 +150,14 @@ Also EJB's might be moved between machines in a cluster and only managed resourc diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DoNotUseThreads.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DoNotUseThreads.xml index fa658bee70..ef48c8a494 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DoNotUseThreads.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DoNotUseThreads.xml @@ -108,6 +108,15 @@ public class Violation { } }; } +} + ]]> + + + getContextClassLoader is ok + 0 + From c106f04fcf4b51851a3d8bcc7b8049b4f1ec6201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 4 Nov 2020 08:11:22 +0100 Subject: [PATCH 129/299] Doc and tests for InvocationMatcher --- docs/_data/xpath_funs.yml | 25 ++++ ...nitAssertionsShouldIncludeMessageRule.java | 26 ++-- .../internal/MatchesSignatureFunction.java | 7 +- .../pmd/lang/java/types/TypeTestUtil.java | 111 ++++++++++++------ .../java/types/InvocationMatcherTest.java | 90 ++++++++++++++ 5 files changed, 203 insertions(+), 56 deletions(-) create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java diff --git a/docs/_data/xpath_funs.yml b/docs/_data/xpath_funs.yml index 4780f462a9..32b97e219f 100644 --- a/docs/_data/xpath_funs.yml +++ b/docs/_data/xpath_funs.yml @@ -121,3 +121,28 @@ langs: examples: - code: '//MethodDeclaration[pmd-java:explicitModifiers() = "public"]' outcome: "Matches method declarations that have an explicit 'public' modifier." + + - name: matchesSig + returnType: "xs:boolean" + shortDescription: "Matches the signature called by a method or constructor call" + description: >- + Uses an {% jdoc java::lang.java.types.TypeTestUtil.InvocationMatcher %} to test + the method signature called by the context node. The format of the parameter is + described on that class. + + notes: "The context node must be an {% jdoc jast::InvocationNode %}" + parameters: + - name: "sig" + type: "xs:string" + description: "A signature, in the format described on {% jdoc java::lang.java.types.TypeTestUtil.InvocationMatcher %}" + examples: + - code: '//MethodCall[pmd-java:matchesSig("_#equals(java.lang.Object)")]' + outcome: "Matches calls to the method `equals` on any receiver type" + - code: '//MethodCall[pmd-java:matchesSig("java.lang.Enum#equals(java.lang.Object)")]' + outcome: "Matches calls to the method `equals` on receiver that is a subtype of Enum" + - code: '//MethodCall[pmd-java:matchesSig("java.lang.String#toString()")]' + outcome: "Matches calls to the method `toString` on String receivers" + - code: '//MethodCall[pmd-java:matchesSig("_#_(int,int)")]' + outcome: "Matches calls to any method with 2 `int` parameters (!= argument)" + - code: '//ConstructorCall[pmd-java:matchesSig("java.util.ArrayList#new(int)")]' + outcome: "Matches constructors calls of ArrayList with a single int parameter" diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java index 0ef169c1f7..329021d010 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java @@ -17,20 +17,18 @@ public class JUnitAssertionsShouldIncludeMessageRule extends AbstractJavaRulecha private final List checks = listOf( - InvocationMatcher.parse("_", "assertEquals(_,_)"), - - InvocationMatcher.parse("_", "assertTrue(_)"), - InvocationMatcher.parse("_", "assertFalse(_)"), - InvocationMatcher.parse("_", "assertSame(_,_)"), - InvocationMatcher.parse("_", "assertNotSame(_,_)"), - InvocationMatcher.parse("_", "assertNull(_)"), - InvocationMatcher.parse("_", "assertNotNull(_)"), - - InvocationMatcher.parse("_", "assertArrayEquals(_,_)"), - InvocationMatcher.parse("_", "assertThat(_,_)"), - InvocationMatcher.parse("_", "fail()"), - InvocationMatcher.parse("_", "assertEquals(float,float,float)"), - InvocationMatcher.parse("_", "assertEquals(double,double,double)") + InvocationMatcher.parse("_#assertEquals(_,_)"), + InvocationMatcher.parse("_#assertTrue(_)"), + InvocationMatcher.parse("_#assertFalse(_)"), + InvocationMatcher.parse("_#assertSame(_,_)"), + InvocationMatcher.parse("_#assertNotSame(_,_)"), + InvocationMatcher.parse("_#assertNull(_)"), + InvocationMatcher.parse("_#assertNotNull(_)"), + InvocationMatcher.parse("_#assertArrayEquals(_,_)"), + InvocationMatcher.parse("_#assertThat(_,_)"), + InvocationMatcher.parse("_#fail()"), + InvocationMatcher.parse("_#assertEquals(float,float,float)"), + InvocationMatcher.parse("_#assertEquals(double,double,double)") ); public JUnitAssertionsShouldIncludeMessageRule() { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java index baa50d7551..4bc4baa301 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java @@ -24,13 +24,8 @@ public final class MatchesSignatureFunction extends BaseRewrittenFunction * java.lang.String#toString() // match calls to toString on String instances * _#toString() // match calls to toString on any receiver - * _#_() // match all calls to a method with no arguments - * _#toString(_*) // match calls to a "toString" method with any number of arguments + * _#_() // match all calls to a method with no parameters + * _#toString(_*) // match calls to a "toString" method with any number of parameters * _#eq(_, _) // match calls to an "eq" method that has 2 parameters of unspecified type * _#eq(java.lang.String, _) // like the previous, but the first parameter must be String + * java.util.ArrayList#new(int) // match constructor calls of this overload of the ArrayList constructor * * - *

The receiver parameter (first half) is matched against the - * static type of the receiver of the call (not the declaration site - * of the method). The parameters + *

The receiver matcher (first half) is matched against the + * static type of the receiver of the call, and not the + * declaration site of the method, unless the called method is + * static, or a constructor. + * + *

The parameters are matched against the declared parameters + * types of the called overload, and not the actual argument types. + * In particular, for vararg methods, the signature should mention + * a single parameter, with an array type. + * + *

For example {@code Integer.valueOf('0')} will be matched by + * {@code _#valueOf(int)} but not {@code _#valueOf(char)}, which is + * an overload that does not exist (the char is widened to an int, + * so the int overload is selected). + * + *

Full EBNF grammar (no whitespace is tolerated anywhere): + *

{@code
+     * sig         ::= type '#' method_name param_list
+     * type        ::= qname ( '[]' )* | '_'
+     * method_name ::= '_' | ident | 'new'
+     * param_list  ::= '(_*)' | '(' type (',' type )* ')'
+     * qname       ::= java binary name
+     * }
*/ public static final class InvocationMatcher { @@ -328,17 +349,25 @@ public final class TypeTestUtil { return true; } if (node instanceof ASTConstructorCall) { - return qualifierMatcher.matches(((ASTConstructorCall) node).getTypeNode().getTypeMirror(), true); - } else if (node instanceof QualifiableExpression) { + JTypeMirror newType = ((ASTConstructorCall) node).getTypeNode().getTypeMirror(); + return qualifierMatcher.matches(newType, true); + } + JMethodSig m = node.getMethodType(); + JTypeMirror qualType; + if (node instanceof QualifiableExpression) { ASTExpression qualifier = ((QualifiableExpression) node).getQualifier(); if (qualifier != null) { - return qualifierMatcher.matches(qualifier.getTypeMirror(), false); + qualType = qualifier.getTypeMirror(); + } else { + // todo: if qualifier == null, then we should take the type of the + // implicit receiver, ie `this` or `SomeOuter.this` + qualType = m.getDeclaringType(); } - // todo: if qualifier == null, then we should take the type of the - // implicit receiver, ie `this` or `SomeOuter.this` + } else { + qualType = m.getDeclaringType(); } - return qualifierMatcher.matches(node.getMethodType().getDeclaringType(), false); + return qualifierMatcher.matches(qualType, m.isStatic()); } private boolean argsMatchOverload(JMethodSig invoc) { @@ -359,35 +388,25 @@ public final class TypeTestUtil { /** - * Parses an {@link InvocationMatcher} from two strings. + * Parses an {@link InvocationMatcher}. * - *

The examples on the javadoc of this class are given in a - * syntax, where the first parameter of this method is before the #, - * and the second is after. - * - * @param qualifierMatcher Type matcher for the receiver type (either "_", or a qualified name). - * @param sig A signature in the form {@code name(arg1, arg2, ...)}, - * where each {@code argi} is either {@code _} or a qualified - * type name, without type arguments. + * @param sig A signature in the format described on this class * * @return A sig matcher * * @throws IllegalArgumentException If the parameters are malformed * @throws NullPointerException If the parameters are null */ - public static InvocationMatcher parse(String qualifierMatcher, String sig) { - AssertionUtil.assertValidJavaBinaryName(qualifierMatcher); - int i = 0; - while (i < sig.length() && Character.isJavaIdentifierPart(sig.charAt(i))) { - i++; - } - final String methodName = sig.substring(0, i); - if (methodName.isEmpty()) { - throw new IllegalArgumentException("Not a valid signature " + sig); - } + public static InvocationMatcher parse(String sig) { + int i = parseType(sig, 0); + final TypeMatcher qualifierMatcher = newMatcher(sig.substring(0, i)); + i = consumeChar(sig, i, '#'); + final int nameStart = i; + i = parseSimpleName(sig, i); + final String methodName = sig.substring(nameStart, i); i = consumeChar(sig, i, '('); if (isChar(sig, i, ')')) { - return new InvocationMatcher(newMatcher(qualifierMatcher), methodName, Collections.emptyList()); + return new InvocationMatcher(qualifierMatcher, methodName, Collections.emptyList()); } // (_*) matches any argument list List argMatchers; @@ -403,7 +422,18 @@ public final class TypeTestUtil { if (i != sig.length()) { throw new IllegalArgumentException("Not a valid signature " + sig); } - return new InvocationMatcher(newMatcher(qualifierMatcher), methodName, argMatchers); + return new InvocationMatcher(qualifierMatcher, methodName, argMatchers); + } + + private static int parseSimpleName(String sig, final int start) { + int i = start; + while (i < sig.length() && Character.isJavaIdentifierPart(sig.charAt(i))) { + i++; + } + if (i == start) { + throw new IllegalArgumentException("Not a valid signature " + sig); + } + return i; } private static int parseArgList(String sig, int i, List argMatchers) { @@ -429,14 +459,23 @@ public final class TypeTestUtil { } private static int parseType(String source, int i, List result) { + final int start = i; + i = parseType(source, i); + result.add(newMatcher(source.substring(start, i))); + return i; + } + + private static int parseType(String source, int i) { final int start = i; while (i < source.length() && (Character.isJavaIdentifierPart(source.charAt(i)) || source.charAt(i) == '.')) { i++; } - String name = source.substring(start, i); - AssertionUtil.assertValidJavaBinaryName(name); - result.add(newMatcher(name)); + AssertionUtil.assertValidJavaBinaryName(source.substring(start, i)); + // array dimensions + while (isChar(source, i, '[')) { + i = consumeChar(source, i + 1, ']'); + } return i; } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java new file mode 100644 index 0000000000..08b1dc76c5 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java @@ -0,0 +1,90 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.types; + +import static net.sourceforge.pmd.lang.java.types.TypeTestUtil.InvocationMatcher.parse; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.InvocationNode; +import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest; + +public class InvocationMatcherTest extends BaseNonParserTest { + + @Rule + public final ExpectedException expect = ExpectedException.none(); + + @Test + public void testSimpleMatcher() { + + ASTMethodCall call = + java.parse("class Foo {{ Integer.valueOf('c'); }}") + .descendants(ASTMethodCall.class).firstOrThrow(); + + assertMatch(call, "_#valueOf(int)"); + assertMatch(call, "java.lang.Integer#valueOf(int)"); + assertMatch(call, "java.lang.Integer#_(int)"); + assertMatch(call, "java.lang.Integer#_(_*)"); + + assertNoMatch(call, "java.lang.Integer#valueOf(char)"); + assertNoMatch(call, "java.lang.Integer#valueOf2(_*)"); + assertNoMatch(call, "java.lang.Object#valueOf(_*)"); + } + + @Test + public void testCtorMatchers() { + + ASTConstructorCall call = + java.parse("class Foo {{ new java.util.ArrayList('c'); }}") + .descendants(ASTConstructorCall.class).firstOrThrow(); + + assertMatch(call, "_#new(int)"); + assertMatch(call, "java.util.ArrayList#new(int)"); + assertMatch(call, "java.util.ArrayList#_(int)"); + assertMatch(call, "java.util.ArrayList#_(_*)"); + + assertNoMatch(call, "java.util.ArrayList#new()"); + assertNoMatch(call, "java.util.ArrayList#_()"); + + assertNoMatch(call, "java.util.List#new(_*)"); + assertNoMatch(call, "java.util.List#_(_*)"); + assertNoMatch(call, "java.lang.Object#new(int)"); + } + + @Test + public void testArray() { + + ASTMethodCall call = + java.parse("class Foo {{ new int[0].toString(); }}") + .descendants(ASTMethodCall.class).firstOrThrow(); + + assertMatch(call, "int[]#toString()"); + assertMatch(call, "_#toString()"); + assertMatch(call, "int[]#_()"); + assertMatch(call, "int[]#_(_*)"); + assertMatch(call, "_#_(_*)"); + + assertNoMatch(call, "_#new(int)"); + assertNoMatch(call, "_[][]#_(_*)"); + // maybe we should support this one later + assertNoMatch(call, "_[]#toString()"); + } + + private void assertMatch(InvocationNode call, String sig) { + Assert.assertTrue(sig + " should match " + call, + parse(sig).matchesCall(call)); + } + + private void assertNoMatch(InvocationNode call, String s) { + Assert.assertFalse(s + " should not match " + call, + parse(s).matchesCall(call)); + } + +} From 149d3732f85b77a4faef05ba933c84aa49435868 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 4 Nov 2020 09:17:17 +0100 Subject: [PATCH 130/299] Fix soft assertions for JUnitTestsShouldIncludeAssertTest --- .../bestpractices/JUnitTestsShouldIncludeAssertRule.java | 8 +++++++- .../net/sourceforge/pmd/lang/java/types/TypeTestUtil.java | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index 394ba95b3b..e46b85178c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -10,6 +10,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil; +import net.sourceforge.pmd.lang.java.types.TypeTestUtil; public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule { @@ -33,7 +34,7 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule private static boolean isProbableAssertCall(ASTMethodCall call) { String name = call.getMethodName(); - return name.startsWith("assert") + return name.startsWith("assert") && !isSoftAssert(call) || name.startsWith("check") || name.startsWith("verify") || name.equals("fail") @@ -41,6 +42,11 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule || JUnitRuleUtil.isExpectExceptionCall(call); } + private static boolean isSoftAssert(ASTMethodCall call) { + return TypeTestUtil.isA("org.assertj.core.api.AbstractSoftAssertions", call.getMethodType().getDeclaringType()) + && !"assertAll".equals(call.getMethodName()); + } + /** * Tells if the node contains a Test annotation with an expected exception. */ diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index 0106a13bf0..b51b629373 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -118,7 +118,7 @@ public final class TypeTestUtil { return isA(canonicalName, node.getTypeMirror(), unresolvedStore); } - private static boolean isA(@NonNull String canonicalName, @Nullable JTypeMirror thisType) { + public static boolean isA(@NonNull String canonicalName, @Nullable JTypeMirror thisType) { AssertionUtil.requireParamNotNull("canonicalName", (Object) canonicalName); if (thisType == null) { return false; From 4dbd161144aad0aebb5ec240e5576f5da04ebdd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 4 Nov 2020 09:19:07 +0100 Subject: [PATCH 131/299] Fix ClassNamingConventions --- .../lang/java/rule/codestyle/ClassNamingConventionsRule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java index 8055d987f4..73f57c8050 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/codestyle/ClassNamingConventionsRule.java @@ -30,7 +30,7 @@ public class ClassNamingConventionsRule extends AbstractNamingConventionRule Date: Wed, 4 Nov 2020 09:29:16 +0100 Subject: [PATCH 132/299] Fix test compil --- .../net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java index 2030d30b3d..860c96ca50 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java @@ -229,7 +229,8 @@ public class TypeTestUtilTest extends BaseNonParserTest { @Test public void testNullNode() { Assert.assertFalse(TypeTestUtil.isA(String.class, null)); - Assert.assertFalse(TypeTestUtil.isA("java.lang.String", null)); + Assert.assertFalse(TypeTestUtil.isA("java.lang.String", (JTypeMirror) null)); + Assert.assertFalse(TypeTestUtil.isA("java.lang.String", (TypeNode) null)); Assert.assertFalse(TypeTestUtil.isExactlyA(String.class, (TypeNode) null)); Assert.assertFalse(TypeTestUtil.isExactlyA("java.lang.String", null)); } From 3b29ceb13bd5d5b82d08e184405739d0b0a3b2d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 4 Nov 2020 09:37:27 +0100 Subject: [PATCH 133/299] Fix PMD warnings --- .../rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index e46b85178c..4daa9c7ad7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -37,8 +37,8 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule return name.startsWith("assert") && !isSoftAssert(call) || name.startsWith("check") || name.startsWith("verify") - || name.equals("fail") - || name.equals("failWith") + || "fail".equals(name) + || "failWith".equals(name) || JUnitRuleUtil.isExpectExceptionCall(call); } From c18509cab06464a577cf2e64ef720c41794d6df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 4 Nov 2020 17:50:52 +0100 Subject: [PATCH 134/299] Fix report location of enum constant --- .../net/sourceforge/pmd/lang/java/ast/ASTEnumConstant.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumConstant.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumConstant.java index f95b7d8002..0bdd24e0eb 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumConstant.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTEnumConstant.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.ast; import org.checkerframework.checker.nullness.qual.Nullable; +import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; import net.sourceforge.pmd.lang.java.types.OverloadSelectionResult; /** @@ -31,6 +32,10 @@ public final class ASTEnumConstant extends AbstractJavaTypeNode super(id); } + @Override + protected @Nullable JavaccToken getPreferredReportLocation() { + return getVarId().getFirstToken(); + } @Override protected R acceptVisitor(JavaVisitor visitor, P data) { From 2095d2f63dc584e36f92199d8f968331c8e8432d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:14:29 +0100 Subject: [PATCH 135/299] Check fixes #2712 --- .../rule/performance/xml/SimplifyStartsWith.xml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml index fa36e3c7a0..69e3638b73 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/SimplifyStartsWith.xml @@ -69,6 +69,21 @@ public class SimplifyStartsWith { String getText() { return ""; } } +} + ]]> + + + #2712 SimplifyStartsWith false-positive on receiver != String + 0 + From e912f12124e691d0faf035c429d83196930e000c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:16:38 +0100 Subject: [PATCH 136/299] Check fixes #2883 --- .../xml/JUnitAssertionsShouldIncludeMessage.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml index 7b49484f9c..043a2dcdc3 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml @@ -433,6 +433,23 @@ class SimpleTest { public void simpleMethod() { assertEquals(0, Integer.compare(0, 0), "Not equals 0 != 1"); } +} + ]]> + + + [java] JUnitAssertionsShouldIncludeMessage false positive with method call #2883 + 0 + From a0e1d7577d23ed4aa14e1b89abc334c4a14d5199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:25:31 +0100 Subject: [PATCH 137/299] Update SwitchStmtsShouldHaveDefault --- .travis/all-java.xml | 2 +- .../sourceforge/pmd/util/CollectionUtil.java | 23 ++++++------ .../pmd/lang/java/ast/ASTSwitchLike.java | 35 ++++++++++--------- .../SwitchStmtsShouldHaveDefaultTest.java | 1 - 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/.travis/all-java.xml b/.travis/all-java.xml index dc8933490b..9b1eb2ad13 100644 --- a/.travis/all-java.xml +++ b/.travis/all-java.xml @@ -46,7 +46,7 @@ - + diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java index 366e8fff84..422f2f0dd6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java @@ -459,16 +459,19 @@ public final class CollectionUtil { * @param Type of accumulated values */ public static Collector> toMutableList() { - return Collector., List>of( - ArrayList::new, - ArrayList::add, - (left, right) -> { - left.addAll(right); - return left; - }, - a -> a, - Characteristics.IDENTITY_FINISH - ); + return Collectors.toCollection(ArrayList::new); + } + + + /** + * A collector that returns a mutable set. This contrasts with + * {@link Collectors#toSet()}, which makes no guarantee about the + * mutability of the set. + * + * @param Type of accumulated values + */ + public static Collector> toMutableSet() { + return Collectors.toCollection(HashSet::new); } /** diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLike.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLike.java index 11ad19ce28..caf3aa8a56 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLike.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTSwitchLike.java @@ -7,9 +7,12 @@ package net.sourceforge.pmd.lang.java.ast; import java.util.Iterator; import java.util.Set; -import org.apache.commons.lang3.EnumUtils; - import net.sourceforge.pmd.lang.ast.NodeStream; +import net.sourceforge.pmd.lang.java.symbols.JClassSymbol; +import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; +import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; +import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; +import net.sourceforge.pmd.util.CollectionUtil; /** @@ -38,8 +41,7 @@ public interface ASTSwitchLike extends JavaNode, Iterable { * Returns true if this switch has a {@code default} case. */ default boolean hasDefaultCase() { - ASTSwitchBranch last = getBranches().last(); - return last != null && last.getLabel().isDefault(); + return getBranches().any(it -> it.getLabel().isDefault()); } @@ -67,29 +69,30 @@ public interface ASTSwitchLike extends JavaNode, Iterable { * the tested expression could not be resolved. */ default boolean isExhaustiveEnumSwitch() { - ASTExpression expression = getTestedExpression(); + JTypeDeclSymbol type = getTestedExpression().getTypeMirror().getSymbol(); - if (expression.getType() == null) { + if (!(type instanceof JClassSymbol)) { return false; } - if (Enum.class.isAssignableFrom(expression.getType())) { - - @SuppressWarnings("unchecked") - Set constantNames = EnumUtils.getEnumMap((Class) expression.getType()).keySet(); + if (((JClassSymbol) type).isEnum() && !type.isUnresolved()) { + Set enumConstantNames = ((JClassSymbol) type).getDeclaredFields() + .stream() + .filter(JFieldSymbol::isEnumConstant) + .map(JElementSymbol::getSimpleName) + .collect(CollectionUtil.toMutableSet()); for (ASTSwitchBranch branch : this) { // since this is an enum switch, the labels are necessarily // the simple name of some enum constant. - - // descendant can be null for default case - ASTName name = branch.getLabel().getFirstDescendantOfType(ASTName.class); - if (name != null) { - constantNames.remove(name.getImage()); + for (ASTExpression expr : branch.getLabel().getExprList()) { + if (expr instanceof ASTVariableAccess) { + enumConstantNames.remove(((ASTVariableAccess) expr).getName()); + } } } - return constantNames.isEmpty(); + return enumConstantNames.isEmpty(); } return false; diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SwitchStmtsShouldHaveDefaultTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SwitchStmtsShouldHaveDefaultTest.java index 008181fb35..bbec6a1612 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SwitchStmtsShouldHaveDefaultTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/SwitchStmtsShouldHaveDefaultTest.java @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; import net.sourceforge.pmd.testframework.PmdRuleTst; -@org.junit.Ignore("Rule has not been updated yet") public class SwitchStmtsShouldHaveDefaultTest extends PmdRuleTst { // no additional unit tests } From d5e8c45658e8a9efffb025b273a4c9ded41204f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:28:46 +0100 Subject: [PATCH 138/299] Check fixes #2806 --- .../xml/SwitchStmtsShouldHaveDefault.xml | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/SwitchStmtsShouldHaveDefault.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/SwitchStmtsShouldHaveDefault.xml index 0ecf6177f1..813e52cd08 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/SwitchStmtsShouldHaveDefault.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/SwitchStmtsShouldHaveDefault.xml @@ -78,6 +78,64 @@ public class Foo { break; } } +} + ]]> + + + + Enum type, not ok + 1 + + + + Enum type, not ok (arrow branches) + 1 + { + int y = 8; + } + case B -> throw new IllegalStateException(); + } + } +} + ]]> + + + #2806 [java] SwitchStmtsShouldHaveDefault false-positive with Java 14 non-fallthrough branches + 0 + foo = "eq"; + case "!=" -> foo = "ne"; + default -> throw new IllegalArgumentException(); + } + return foo + 'a'; + } } ]]> From 7c5fbb26ac402991f25837565c0aacbfbec9617b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:30:12 +0100 Subject: [PATCH 139/299] Check fixes #2716 --- .../errorprone/xml/CompareObjectsWithEquals.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index 44138dc29d..9b8e55f9c5 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -227,6 +227,23 @@ public class Obj { final Obj that = (Obj) other; return that.i == this.i; } +} + ]]> + + + [java] CompareObjectsWithEqualsRule: False positive with Enums #2716 + 0 + From ce3d91e01ad088600979740468f79324fa18a20a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:33:57 +0100 Subject: [PATCH 140/299] Check fixes #2147 --- .../xml/JUnitTestsShouldIncludeAssert.xml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml index 3f20347c1a..0323f79fe7 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml @@ -532,6 +532,38 @@ public class FooTest extends TestCase { public void test2() { assertThat("doesn't matter", null, Matchers.nullValue()); } +} + ]]> + + + [java] JUnitTestsShouldIncludeAssert - false positives with lambdas and static methods #2147 + 0 + assertTrue(item.size() <= totalFieldsCount)); + } + + public static JsonNode assertSuccessResponse() { + JsonNode jsonNode = doStuffToBuildTheJsonNode(); + assertTrue(jsonNode.hasNonNull("count")); + assertTrue(jsonNode.hasNonNull("data")); + assertTrue(jsonNode.get("count").asInt() > 0); + assertEquals(jsonNode.get("count").asInt(), jsonNode.get("data").size()); + return jsonNode; + } } ]]> From e799a140ea8d2de5f65d9f45b3e4522570bd27d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:40:50 +0100 Subject: [PATCH 141/299] Check fixes #1969 Actually was fixed by #2800 --- .../a/PackagePrivateMethod.java | 13 +++++++ .../a/PackagePrivateMethodRealExtend.java | 14 ++++++++ .../b/PackagePrivateMethodExtend.java | 14 ++++++++ .../bestpractices/xml/MissingOverride.xml | 34 +++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethod.java create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethodRealExtend.java create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/b/PackagePrivateMethodExtend.java diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethod.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethod.java new file mode 100644 index 0000000000..7236e9d73d --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethod.java @@ -0,0 +1,13 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices.missingoverride.a; + +public class PackagePrivateMethod { + + // package private + void printMessage() { + System.out.println("Click"); + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethodRealExtend.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethodRealExtend.java new file mode 100644 index 0000000000..2f5ab59a9b --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/a/PackagePrivateMethodRealExtend.java @@ -0,0 +1,14 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices.missingoverride.a; + +public class PackagePrivateMethodRealExtend extends PackagePrivateMethod { + + // package private, does override + @Override + void printMessage() { + System.out.println("Click"); + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/b/PackagePrivateMethodExtend.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/b/PackagePrivateMethodExtend.java new file mode 100644 index 0000000000..1ff65f10a8 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/missingoverride/b/PackagePrivateMethodExtend.java @@ -0,0 +1,14 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices.missingoverride.b; + +import net.sourceforge.pmd.lang.java.rule.bestpractices.missingoverride.a.PackagePrivateMethod; + +public class PackagePrivateMethodExtend extends PackagePrivateMethod { + // does not override + void printMessage() { + System.out.println("Hack"); + } +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml index 16098caa2f..70b797df76 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/MissingOverride.xml @@ -573,4 +573,38 @@ public record Point(int x, int y) { ]]> java 14-preview + + Package private method cannot be overridden outside of its package #1969 (1) + 1 + + + + Package private method cannot be overridden outside of its package #1969 (2) + 0 + + From 3d7de24256ccc239ef497e36d5b6a3ae6bb0ef5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:46:54 +0100 Subject: [PATCH 142/299] Check fixes #1565 --- .../xml/JUnitAssertionsShouldIncludeMessage.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml index 043a2dcdc3..6a2915da3f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitAssertionsShouldIncludeMessage.xml @@ -450,6 +450,23 @@ public class AssertionMessageTest { assertEquals(e.toString(), expectedLength, e.getMessage().length()); // no warning expected } } +} + ]]> + + + JUnitAssertionsShouldIncludeMessage false positive with AssertJ #1565 + 0 + ()).as("Should return database entries as map") + .hasSize(3) + .containsEntry(3, 4); + } + } ]]> From 06664d28872818770b3bc0cac23348c8dc486715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:50:05 +0100 Subject: [PATCH 143/299] Check fixes #1422 --- .../xml/JUnitTestsShouldIncludeAssert.xml | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml index 0323f79fe7..e6ab530c82 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/JUnitTestsShouldIncludeAssert.xml @@ -567,4 +567,78 @@ public class FooTest extends TestCase { } ]]> + + [java] JUnitTestsShouldIncludeAssert false positive with inherited @Rule field #1422 + 0 + + From c37232368ee030d19d329c1204d94ab2b9b79a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:51:58 +0100 Subject: [PATCH 144/299] Check fixes #1005 --- .../xml/CloneMethodMustImplementCloneable.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloneMethodMustImplementCloneable.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloneMethodMustImplementCloneable.xml index 868f2dcbdb..f7211117b1 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloneMethodMustImplementCloneable.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloneMethodMustImplementCloneable.xml @@ -186,4 +186,14 @@ class CloneableClass implements TestInterface { } ]]> + + [java] CloneMethodMustImplementCloneable triggers for interfaces #1005 + 0 + + From ff33b022f65154e87e233e594763bde782f42fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:54:21 +0100 Subject: [PATCH 145/299] Check fixes #807 --- .../xml/AccessorMethodGeneration.xml | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml index 5af1d09cd6..ace3cc8c7f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml @@ -264,4 +264,50 @@ class Outer { ]]> java 10 + + AccessorMethodGeneration false positive with overloads #807 + 0 + + java 10 + + + AccessorMethodGeneration false positive with overloads #807 (control test) + 1 + + java 10 + From 4a1b30bef7bcbe83916c426002ce379f2ac0b227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:55:20 +0100 Subject: [PATCH 146/299] Check fixes #342 --- .../xml/AccessorMethodGeneration.xml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml index ace3cc8c7f..d3fde8ab50 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorMethodGeneration.xml @@ -310,4 +310,31 @@ class Outer { ]]> java 10 + + AccessorMethodGeneration: Name clash with another public field not properly handled #342 + 0 + + java 10 + From 9e7ad67a84ebe96814b717133feeaff8f920871d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 10:58:18 +0100 Subject: [PATCH 147/299] Check fixes #2542 --- .../xml/UseCollectionIsEmpty.xml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml index 0576bea05d..0fa0ee40d8 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UseCollectionIsEmpty.xml @@ -356,6 +356,29 @@ public class Foo { public int size() { return 0; } +} + ]]> + + + #2542 UseCollectionIsEmpty can not detect the case foo.bar().size() + 1 + list = new ArrayList<>(); + + Foo foo() { return this; } + Foo bar() { return this; } + List list() { return list; } + + public void bar() { + if (foo().bar().foo().list().size() == 0) { + throw new RuntimeException("Empty list"); + } + } } ]]> From 2144e72235173f71df156759d5357a42fec16189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 11:02:43 +0100 Subject: [PATCH 148/299] Fix #1998 --- .../AccessorClassGenerationRule.java | 11 ++++++++++- .../AccessorMethodGenerationRule.java | 2 +- .../bestpractices/xml/AccessorClassGeneration.xml | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java index 25b35e7ed9..2828b0d36a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorClassGenerationRule.java @@ -9,6 +9,7 @@ import java.util.Set; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; +import net.sourceforge.pmd.lang.java.ast.ASTExplicitConstructorInvocation; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; @@ -32,7 +33,7 @@ public class AccessorClassGenerationRule extends AbstractJavaRulechainRule { private final Set reportedNodes = new HashSet<>(); public AccessorClassGenerationRule() { - super(ASTConstructorCall.class); + super(ASTConstructorCall.class, ASTExplicitConstructorInvocation.class); } @Override @@ -48,4 +49,12 @@ public class AccessorClassGenerationRule extends AbstractJavaRulechainRule { } return null; } + + @Override + public Object visit(ASTExplicitConstructorInvocation node, Object data) { + if (node.isSuper()) { + AccessorMethodGenerationRule.checkMemberAccess(this, (RuleContext) data, node, node.getMethodType().getSymbol(), this.reportedNodes); + } + return null; + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java index 34f188262b..f73beab33a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AccessorMethodGenerationRule.java @@ -69,7 +69,7 @@ public class AccessorMethodGenerationRule extends AbstractJavaRulechainRule { checkMemberAccess(this, data, node, symbol, this.reportedNodes); } - static void checkMemberAccess(AbstractRule rule, RuleContext data, ASTExpression refExpr, JAccessibleElementSymbol sym, Set reportedNodes) { + static void checkMemberAccess(AbstractRule rule, RuleContext data, JavaNode refExpr, JAccessibleElementSymbol sym, Set reportedNodes) { if (Modifier.isPrivate(sym.getModifiers()) && !Objects.equals(sym.getEnclosingClass(), refExpr.getEnclosingType().getSymbol())) { diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorClassGeneration.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorClassGeneration.xml index a56faedb7f..7bc0e88f3a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorClassGeneration.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/AccessorClassGeneration.xml @@ -156,4 +156,19 @@ public class Foo { ]]> java 10 + + AccessorClassGeneration false-negative: subclass calls private constructor #1998 + 1 + + java 10 + From edaff363a9ceaef6c52f33e7819b29d50750862f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 11:04:20 +0100 Subject: [PATCH 149/299] Check fixes #1224 --- .../xml/InefficientEmptyStringCheck.xml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientEmptyStringCheck.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientEmptyStringCheck.xml index 77df28a871..2d997d0b81 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientEmptyStringCheck.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientEmptyStringCheck.xml @@ -203,4 +203,36 @@ public class PatternMatchingInstanceof { ]]> java 14-preview + + [java] InefficientEmptyStringCheck false negative in anonymous class #1224 + 1 + 0) { + System.out.println("Non-empty string."); + } + } + + @Override + public void keyPressed(KeyEvent e) { + } + + @Override + public void keyReleased(KeyEvent e) { + } + }); + } + } + ]]> + From c514697fbcbb653dd9dd34695b9d09ab03cf9729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 11:49:01 +0100 Subject: [PATCH 150/299] Simplify AvoidDecimalLiteralsInBigDecimalConstructor Fixes #2532 --- .../main/resources/category/java/errorprone.xml | 8 +------- ...AvoidDecimalLiteralsInBigDecimalConstructor.xml | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 2edd8b2125..864ad41ca8 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -296,13 +296,7 @@ exactly equal to 0.1, as one would expect. Therefore, it is generally recommend diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidDecimalLiteralsInBigDecimalConstructor.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidDecimalLiteralsInBigDecimalConstructor.xml index 6c7527f9a5..9ead7c6e8f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidDecimalLiteralsInBigDecimalConstructor.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidDecimalLiteralsInBigDecimalConstructor.xml @@ -96,6 +96,20 @@ public class Foo { public BigDecimal bar8(Float n) { return new BigDecimal(n); // line 30 } +} + ]]> + + + #2532 Expression as argument + 8 + 5 + From 7165ed147905206458f2ea4920af5aa5d5b63d0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 5 Nov 2020 14:38:21 +0100 Subject: [PATCH 151/299] Fix tests --- .../xml/AvoidDecimalLiteralsInBigDecimalConstructor.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidDecimalLiteralsInBigDecimalConstructor.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidDecimalLiteralsInBigDecimalConstructor.xml index 9ead7c6e8f..240559a14a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidDecimalLiteralsInBigDecimalConstructor.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/AvoidDecimalLiteralsInBigDecimalConstructor.xml @@ -101,7 +101,7 @@ public class Foo { #2532 Expression as argument - 8 + 1 5 Date: Sat, 7 Nov 2020 14:45:28 +0100 Subject: [PATCH 152/299] Fix merge --- .../java/rule/errorprone/CompareObjectsWithEqualsRule.java | 3 ++- .../java/rule/multithreading/DoubleCheckedLockingRule.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java index 29cd781809..a2922fcc3c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java @@ -13,6 +13,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTThisExpression; +import net.sourceforge.pmd.lang.java.ast.BinaryOp; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; @@ -29,7 +30,7 @@ public class CompareObjectsWithEqualsRule extends AbstractJavaRulechainRule { @Override public Object visit(ASTInfixExpression node, Object data) { - if (!node.getOperator().isEquality()) { + if (!node.getOperator().hasSamePrecedenceAs(BinaryOp.EQ)) { return data; } ASTExpression left = node.getLeftOperand(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java index 999ac2572b..04427bf008 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/multithreading/DoubleCheckedLockingRule.java @@ -21,6 +21,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTPrimitiveType; import net.sourceforge.pmd.lang.java.ast.ASTReturnStatement; import net.sourceforge.pmd.lang.java.ast.ASTSynchronizedStatement; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; +import net.sourceforge.pmd.lang.java.ast.BinaryOp; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JLocalVariableSymbol; @@ -150,7 +151,7 @@ public class DoubleCheckedLockingRule extends AbstractJavaRule { private boolean isNullCheck(ASTExpression expr, JVariableSymbol var) { if (expr instanceof ASTInfixExpression) { ASTInfixExpression condition = (ASTInfixExpression) expr; - if (condition.getOperator().isEquality()) { + if (condition.getOperator().hasSamePrecedenceAs(BinaryOp.EQ)) { ASTNullLiteral nullLit = condition.getFirstChildOfType(ASTNullLiteral.class); if (nullLit != null) { ASTExpression otherChild = (ASTExpression) condition.getChild(1 - nullLit.getIndexInParent()); From eb7a171e4332b0c0401d417a16df6de72d4622a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 7 Nov 2020 15:12:16 +0100 Subject: [PATCH 153/299] Fix an error with old metrics code --- .../signature/JavaOperationSignature.java | 52 ++++++------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaOperationSignature.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaOperationSignature.java index 22995d539d..d0f7856266 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaOperationSignature.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/multifile/signature/JavaOperationSignature.java @@ -5,16 +5,13 @@ package net.sourceforge.pmd.lang.java.multifile.signature; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Collectors; import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration; +import net.sourceforge.pmd.lang.java.symbols.JElementSymbol; /** * Signature for an operation. @@ -105,64 +102,49 @@ public final class JavaOperationSignature extends JavaSignature fieldNames = - node.getEnclosingType() - .getDeclarations(ASTFieldDeclaration.class) - .flatMap(ASTFieldDeclaration::getVarIds) - .collect(Collectors.toMap( - f -> { - Matcher matcher = FIELD_NAME_PATTERN.matcher(f.getVariableName()); - return matcher.find() ? matcher.group(1) : f.getVariableName(); - }, - f -> f.getTypeNode().getTypeImage() - )); - - return isGetter(node, fieldNames) || isSetter(node, fieldNames); + private static boolean containerHasFieldNamed(ASTMethodDeclaration m, String nameIgnoringCase) { + return m.getEnclosingType() + .getSymbol() + .getDeclaredFields() + .stream() + .map(JElementSymbol::getSimpleName) + .anyMatch(nameIgnoringCase::equalsIgnoreCase); } /** Attempts to determine if the method is a getter. */ - private static boolean isGetter(ASTMethodDeclaration node, Map fieldNames) { + private static boolean isGetter(ASTMethodDeclaration node) { if (node.getArity() != 0 || node.isVoid()) { return false; } if (node.getName().startsWith("get")) { - return containsIgnoreCase(fieldNames.keySet(), node.getName().substring(3)); + return containerHasFieldNamed(node, node.getName().substring(3)); } else if (node.getName().startsWith("is")) { - return containsIgnoreCase(fieldNames.keySet(), node.getName().substring(2)); + return containerHasFieldNamed(node, node.getName().substring(2)); } - return fieldNames.containsKey(node.getName()); + return containerHasFieldNamed(node, node.getName()); } /** Attempts to determine if the method is a setter. */ - private static boolean isSetter(ASTMethodDeclaration node, Map fieldNames) { + private static boolean isSetter(ASTMethodDeclaration node) { if (node.getArity() != 1 || !node.isVoid()) { return false; } if (node.getName().startsWith("set")) { - return containsIgnoreCase(fieldNames.keySet(), node.getName().substring(3)); + return containerHasFieldNamed(node, node.getName().substring(3)); } - return fieldNames.containsKey(node.getName()); - } - - - private static boolean containsIgnoreCase(Set set, String str) { - for (String s : set) { - if (str.equalsIgnoreCase(s)) { - return true; - } - } - return false; + return containerHasFieldNamed(node, node.getName()); } } } From f6af40bd24e89554a8c5146b666ceb54f04f8c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 7 Nov 2020 15:39:38 +0100 Subject: [PATCH 154/299] Fix error in type res --- .../table/internal/SymbolTableResolver.java | 11 +++- .../lang/java/ast/TypeDisambiguationTest.kt | 3 - .../types/internal/infer/AnonCtorsTest.kt | 62 ++++++++++++++++--- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java index 8203afd67b..25cf5b4fae 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/table/internal/SymbolTableResolver.java @@ -16,6 +16,7 @@ import java.util.Set; import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.NodeStream; @@ -367,7 +368,10 @@ public final class SymbolTableResolver { // the varId is only in scope in the body and not the iterable expr setTopSymbolTableAndRecurse(node.getIterableExpr(), ctx); - int pushed = pushOnStack(f.localVarSymTable(top(), enclosing(), node.getVarId().getSymbol())); + ASTVariableDeclaratorId varId = node.getVarId(); + acceptIfNotNull(varId.getTypeNode(), ctx); + + int pushed = pushOnStack(f.localVarSymTable(top(), enclosing(), varId.getSymbol())); ASTStatement body = node.getBody(); if (body instanceof ASTBlock) { // if it's a block then it will be set body.acceptVisitor(this, ctx); @@ -389,6 +393,11 @@ public final class SymbolTableResolver { return null; } + void acceptIfNotNull(@Nullable JavaNode node, ReferenceCtx ctx) { + if (node != null) { + node.acceptVisitor(this, ctx); + } + } @Override public Void visit(ASTTryStatement node, @NonNull ReferenceCtx ctx) { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TypeDisambiguationTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TypeDisambiguationTest.kt index 17e0b089ce..a1490ebd1a 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TypeDisambiguationTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/ast/TypeDisambiguationTest.kt @@ -11,11 +11,8 @@ import net.sourceforge.pmd.lang.ast.test.shouldBe import net.sourceforge.pmd.lang.java.symbols.JClassSymbol import net.sourceforge.pmd.lang.java.symbols.table.internal.SemanticChecksLogger import net.sourceforge.pmd.lang.java.types.JClassType -import net.sourceforge.pmd.lang.java.types.TypeOps import kotlin.test.assertEquals -import kotlin.test.assertNotNull import kotlin.test.assertNull -import kotlin.test.assertTrue class TypeDisambiguationTest : ParserTestSpec({ diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/AnonCtorsTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/AnonCtorsTest.kt index 3bb43adad1..29fcec8816 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/AnonCtorsTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/AnonCtorsTest.kt @@ -180,7 +180,7 @@ class AnonCtorsTest : ProcessorTestSpec({ val (acu, spy) = parser.parseWithTypeInferenceSpy( """ - + class Scratch { class Inner {} @@ -293,7 +293,7 @@ class AnonCtorsTest : ProcessorTestSpec({ val (acu, spy) = parser.parseWithTypeInferenceSpy( """ - + package p.q; class Scratch { @@ -313,19 +313,19 @@ class AnonCtorsTest : ProcessorTestSpec({ } }; } - - static class Foo { - + + static class Foo { + // this return type is ambiguous static p.q.Scratch getScratch() { return new Scratch<>(); } - + Q fooField; } } """) val (t_Scratch, t_Inner, t_Anon, t_Foo) = acu.declaredTypeSignatures() - + val call = acu.descendants(ASTConstructorCall::class.java).firstOrThrow() val fieldAccess = acu.descendants(ASTVariableAccess::class.java).crossFindBoundaries().firstOrThrow() @@ -495,4 +495,52 @@ class AnonCtorsTest : ProcessorTestSpec({ } } + parserTest("Disambiguation of foreach when deferred") { + enableProcessing() + + val (acu, spy) = parser.parseWithTypeInferenceSpy(""" +package p; +import java.util.function.Consumer; +class Assert { + static void main(String... args) { + for (String s : args) { + // body of the runnable is deferred, + // when it's processed, the type of the consumer + // depends on the type of `s`, so the foreach node + // needs to have been disambiged + foo(s, new Consumer<>() { }); + } + } + + static void foo(T a, Consumer i) {} +} + """) + + spy.shouldBeOk { + acu.descendants(ASTConstructorCall::class.java) + .firstOrThrow() shouldHaveType java.util.function.Consumer::class[gen.t_String] + } + } + + parserTest("Disambiguation of when deferred, local var decl") { + enableProcessing() + + val (acu, spy) = parser.parseWithTypeInferenceSpy(""" +package p; +import java.util.function.Consumer; +class Assert { + static void main(String... args) { + String s = args[0]; + foo(s, new Consumer<>() { }); + } + + static void foo(T a, Consumer i) {} +} + """) + + spy.shouldBeOk { + acu.descendants(ASTConstructorCall::class.java) + .firstOrThrow() shouldHaveType java.util.function.Consumer::class[gen.t_String] + } + } }) From 897b5613eac00514b0909d3cb0d8b18789a1498c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 8 Nov 2020 09:01:19 +0100 Subject: [PATCH 155/299] Fix type vars with unresolved bounds being subtypes of anything --- .../pmd/lang/java/types/TypeTestUtil.java | 44 ++++++++++--------- .../pmd/lang/java/types/TypeTestUtilTest.java | 17 +++++++ 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index b51b629373..4df3d730d5 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -89,7 +89,7 @@ public final class TypeTestUtil { return isExactlyA(clazz, type.getSymbol()); } - return type.isSubtypeOf(otherType); + return isA(type, otherType); } @@ -127,17 +127,35 @@ public final class TypeTestUtil { return isA(canonicalName, thisType, null); } - private static boolean isA(@NonNull String canonicalName, @NonNull JTypeMirror thisType, @Nullable UnresolvedClassStore unresolvedStore) { + /** + * This is the subtyping routine we use, which prunes some behavior + * of isSubtypeOf that we don't want (eg, that unresolved types are + * subtypes of everything). + */ + private static boolean isA(JTypeMirror t1, JTypeMirror t2) { + if (t1 == null || t2 == null) { + return false; + } else if (t1.isPrimitive() || t2.isPrimitive()) { + return t1.equals(t2); // isSubtypeOf considers primitive widening like subtyping + } else if (TypeOps.isUnresolved(t1)) { + // we can't get any useful info from this, isSubtypeOf would return true + return false; + } else if (t2.isClassOrInterface() && ((JClassType) t2).getSymbol().isAnonymousClass()) { + return false; // conventionally + } else if (t1 instanceof JTypeVar) { + return t2.isTop() || isA(((JTypeVar) t1).getUpperBound(), t2); + } + return t1.isSubtypeOf(t2); + } + + private static boolean isA(@NonNull String canonicalName, @NonNull JTypeMirror thisType, @Nullable UnresolvedClassStore unresolvedStore) { OptionalBool exactMatch = isExactlyAOrAnon(canonicalName, thisType); if (exactMatch != OptionalBool.NO) { return exactMatch == OptionalBool.YES; // otherwise anon, and we return false } JTypeDeclSymbol thisClass = thisType.getSymbol(); - if (thisClass instanceof JClassSymbol && ((JClassSymbol) thisClass).isAnnotation()) { - return isAnnotationSuperType(canonicalName); - } if (thisClass != null && thisClass.isUnresolved()) { // we can't get any useful info from this, isSubtypeOf would return true @@ -147,22 +165,8 @@ public final class TypeTestUtil { TypeSystem ts = thisType.getTypeSystem(); @Nullable JTypeMirror otherType = TypesFromReflection.loadType(ts, canonicalName, unresolvedStore); - if (otherType == null - || otherType.isClassOrInterface() && ((JClassType) otherType).getSymbol().isAnonymousClass()) { - return false; // we know isExactlyA(canonicalName, node); returned false - } else if (otherType.isPrimitive()) { - return otherType.equals(thisType); // isSubtypeOf considers primitive widening like subtyping - } - return thisType.isSubtypeOf(otherType); - } - - private static boolean isAnnotationSuperType(String clazzName) { - AssertionUtil.assertValidJavaBinaryName(clazzName); - // then, the supertype may only be Object, j.l.Annotation - // this is used e.g. by the typeIs function in XPath - return "java.lang.annotation.Annotation".equals(clazzName) - || "java.lang.Object".equals(clazzName); + return isA(thisType, otherType); } /** diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java index 860c96ca50..db6985ae31 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java @@ -20,6 +20,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTAnonymousClassDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTType; import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest; @@ -159,6 +160,22 @@ public class TypeTestUtilTest extends BaseNonParserTest { assertIsA(klass, Object.class); } + @Test + public void testIsATypeVarWithUnresolvedBound() { + // a type var with an unresolved bound should not be considered + // a subtype of everything + + ASTType field = + java.parse("class Foo {\n" + + "\tT field;\n" + + "}") + .descendants(ASTFieldDeclaration.class) + .firstOrThrow().getTypeNode(); + + assertIsA(field, Object.class); + assertIsNot(field, String.class); + } + @Test public void testIsAStringWithTypeArguments() { From 68d6d644e22e09bb3f072a3a557d61ed24dd381b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 8 Nov 2020 09:13:56 +0100 Subject: [PATCH 156/299] Verify AppendCharacterWithChar handles escapes --- .../rule/performance/xml/AppendCharacterWithChar.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AppendCharacterWithChar.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AppendCharacterWithChar.xml index a6c3759c71..9b917cbec6 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AppendCharacterWithChar.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/AppendCharacterWithChar.xml @@ -28,6 +28,18 @@ public class Foo { ]]> + + appending single char escaped + 1 + + + this is probably wrong, but shouldn't fail 0 From 8d7bc66057ed847b1f449a19b1cac8b911a1476b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 8 Nov 2020 09:22:15 +0100 Subject: [PATCH 157/299] Fix FN with CloneMethodMustImplementCloneable --- .../CloneMethodMustImplementCloneableRule.java | 7 ++++--- .../xml/CloneMethodMustImplementCloneable.xml | 10 ++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java index cf65b80577..3ef6d2c387 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CloneMethodMustImplementCloneableRule.java @@ -29,10 +29,11 @@ public class CloneMethodMustImplementCloneableRule extends AbstractJavaRulechain @Override public Object visit(final ASTMethodDeclaration node, final Object data) { - ASTBlock body = node.getBody(); - if (body == null || !isCloneMethod(node)) { + if (!isCloneMethod(node)) { return data; - } else if (justThrowsCloneNotSupported(body)) { + } + ASTBlock body = node.getBody(); + if (body != null && justThrowsCloneNotSupported(body)) { return data; } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloneMethodMustImplementCloneable.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloneMethodMustImplementCloneable.xml index f7211117b1..871be66e85 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloneMethodMustImplementCloneable.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CloneMethodMustImplementCloneable.xml @@ -170,6 +170,16 @@ interface TestInterface extends Cloneable { ]]> + + #1532 [java] CloneMethodMustImplementCloneable: Implemented Interface extends Cloneable - part 2: interface + 1 + + + #1532 [java] CloneMethodMustImplementCloneable: Implemented Interface extends Cloneable 0 From 140a44a78e918cb20714be4b8fd06092a34b1194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 8 Nov 2020 09:40:23 +0100 Subject: [PATCH 158/299] Fix FNs with LiteralFirstInComparisons --- .../LiteralsFirstInComparisonsRule.java | 11 +++++++++-- .../xml/LiteralsFirstInComparisons.xml | 18 +++++++++++++++++- 2 files changed, 26 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 38de34d0ce..009f1a6376 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 @@ -31,8 +31,8 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRulechainRule { @Override public Object visit(ASTMethodCall call, Object data) { if ("equals".equals(call.getMethodName()) - // not an overload - && call.getMethodType().getFormalParameters().equals(listOf(call.getTypeSystem().OBJECT))) { + && call.getArguments().size() == 1 + && isEqualsObjectAndNotAnOverload(call)) { checkArgs((RuleContext) data, call); } else if (STRING_COMPARISONS.contains(call.getMethodName()) && call.getArguments().size() == 1 @@ -42,6 +42,13 @@ public class LiteralsFirstInComparisonsRule extends AbstractJavaRulechainRule { return data; } + private boolean isEqualsObjectAndNotAnOverload(ASTMethodCall call) { + if (call.getOverloadSelectionInfo().isFailed()) { + return true; // failed selection is considered probably equals(Object) + } + return call.getMethodType().getFormalParameters().equals(listOf(call.getTypeSystem().OBJECT)); + } + private void checkArgs(RuleContext ctx, ASTMethodCall call) { ASTExpression arg = call.getArguments().get(0); ASTExpression qualifier = call.getQualifier(); 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 0c19b02d1c..4155cb0c92 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 @@ -30,7 +30,7 @@ public class Foo { ok, empty literal in .equals comparison - 0 + 4 + + + FN with unresolved types + 2 + From 83450d815fa04b157cf53ad800663248fe53fe28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 8 Nov 2020 09:51:14 +0100 Subject: [PATCH 159/299] Fix FP in RedundantFieldInitializer --- .../RedundantFieldInitializerRule.java | 23 +++++++++++-------- .../xml/RedundantFieldInitializer.xml | 21 ++++++++++++++++- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerRule.java index d5670f13a6..801e1f4ab0 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/performance/RedundantFieldInitializerRule.java @@ -4,18 +4,17 @@ package net.sourceforge.pmd.lang.java.rule.performance; -import org.checkerframework.checker.nullness.qual.NonNull; - import net.sourceforge.pmd.lang.java.ast.ASTBooleanLiteral; import net.sourceforge.pmd.lang.java.ast.ASTExpression; +import net.sourceforge.pmd.lang.java.ast.ASTFieldAccess; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTNullLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.ast.JModifier; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind; import net.sourceforge.pmd.lang.java.types.JTypeMirror; -import net.sourceforge.pmd.lang.rule.RuleTargetSelector; /** * Detects redundant field initializers, i.e. the field initializer expressions @@ -24,12 +23,10 @@ import net.sourceforge.pmd.lang.rule.RuleTargetSelector; * @author lucian.ciufudean@gmail.com * @since Apr 10, 2009 */ -public class RedundantFieldInitializerRule extends AbstractJavaRule { +public class RedundantFieldInitializerRule extends AbstractJavaRulechainRule { - - @Override - protected @NonNull RuleTargetSelector buildTargetSelector() { - return RuleTargetSelector.forTypes(ASTFieldDeclaration.class); + public RedundantFieldInitializerRule() { + super(ASTFieldDeclaration.class); } @Override @@ -52,6 +49,10 @@ public class RedundantFieldInitializerRule extends AbstractJavaRule { if (type.isPrimitive(PrimitiveTypeKind.BOOLEAN)) { return expr instanceof ASTBooleanLiteral && !((ASTBooleanLiteral) expr).isTrue(); } else { + if (!isOkExpr(expr)) { + // whitelist named constants or calculations involving them + return false; + } Object constValue = expr.getConstValue(); return constValue instanceof Number && ((Number) constValue).doubleValue() == 0d || constValue instanceof Character && constValue.equals('\u0000'); @@ -60,4 +61,8 @@ public class RedundantFieldInitializerRule extends AbstractJavaRule { return expr instanceof ASTNullLiteral; } } + + private static boolean isOkExpr(ASTExpression e) { + return e.descendantsOrSelf().none(it -> it instanceof ASTVariableAccess || it instanceof ASTFieldAccess); + } } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/RedundantFieldInitializer.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/RedundantFieldInitializer.xml index 097ad12c1e..7d8d6fbdea 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/RedundantFieldInitializer.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/RedundantFieldInitializer.xml @@ -1434,7 +1434,7 @@ public class RedundantFieldInitializerTest { } ]]> - + #2441 can not detect a case char c = '\0'; 1 @@ -1442,6 +1442,25 @@ public class RedundantFieldInitializerTest { + + + + Non-literals are ok + 1 + 9 + From f1c9387411139fa892b2a4de966c8da1f4c9e82e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 8 Nov 2020 10:05:54 +0100 Subject: [PATCH 160/299] Ensure removed violations of UselessStringValueOf are not FNs --- .../rule/performance/xml/UselessStringValueOf.xml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UselessStringValueOf.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UselessStringValueOf.xml index 66393c8379..30c7e0968d 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UselessStringValueOf.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/UselessStringValueOf.xml @@ -139,6 +139,19 @@ public class Test { private void print(String s) { System.out.println(s); } +} + ]]> + + + FP with char array valueOf + 0 + From bcba3e69897320c92f00653768faf51bbbd7c59e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 8 Nov 2020 10:13:15 +0100 Subject: [PATCH 161/299] Fix fp of UseShortArrayInitializer --- .../src/main/resources/category/java/codestyle.xml | 2 +- .../rule/codestyle/xml/UseShortArrayInitializer.xml | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/pmd-java/src/main/resources/category/java/codestyle.xml b/pmd-java/src/main/resources/category/java/codestyle.xml index fdd1a90a29..036d47bf0c 100644 --- a/pmd-java/src/main/resources/category/java/codestyle.xml +++ b/pmd-java/src/main/resources/category/java/codestyle.xml @@ -1807,7 +1807,7 @@ E.g. `int[] x = new int[] { 1, 2, 3 };` can be written as `int[] x = { 1, 2, 3 } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UseShortArrayInitializer.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UseShortArrayInitializer.xml index e85086f2f0..84da5c63c7 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UseShortArrayInitializer.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/UseShortArrayInitializer.xml @@ -90,6 +90,17 @@ public class UseShortArrayExample { var ar1 = new int[] { 1, 2 }; //var ar2 = { 1, 2 }; // this is actually a compile-time error and is forbidden. See JLS 14.4. } +} + ]]> + + + array initialization with Object LHS type + 0 + From 1df6a92477ec060eeba7925a7a604aa4845a50c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 10 Nov 2020 11:45:05 +0100 Subject: [PATCH 162/299] Update release notes, refs #2899 --- docs/pages/7_0_0_release_notes.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index ab91ec7360..5f20006891 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -96,14 +96,31 @@ The following previously deprecated rules have been finally removed: ### Fixed Issues * java-bestpractices + * [#342](https://github.com/pmd/pmd/issues/342): \[java] AccessorMethodGeneration: Name clash with another public field not properly handled + * [#807](https://github.com/pmd/pmd/issues/807): \[java] AccessorMethodGeneration false positive with overloads + * [#1422](https://github.com/pmd/pmd/issues/1422): \[java] JUnitTestsShouldIncludeAssert false positive with inherited @Rule field + * [#1565](https://github.com/pmd/pmd/issues/1565): \[java] JUnitAssertionsShouldIncludeMessage false positive with AssertJ + * [#1969](https://github.com/pmd/pmd/issues/1969): \[java] MissingOverride false-positive triggered by package-private method overwritten in another package by extending class + * [#1998](https://github.com/pmd/pmd/issues/1998): \[java] AccessorClassGeneration false-negative: subclass calls private constructor + * [#2147](https://github.com/pmd/pmd/issues/2147): \[java] JUnitTestsShouldIncludeAssert - false positives with lambdas and static methods + * [#2542](https://github.com/pmd/pmd/issues/2542): \[java] UseCollectionIsEmpty can not detect the case `foo.bar().size()` * [#2796](https://github.com/pmd/pmd/issue/2796): \[java] UnusedAssignment false positive with call chains * [#2797](https://github.com/pmd/pmd/issues/2797): \[java] MissingOverride long-standing issues + * [#2806](https://github.com/pmd/pmd/issues/2806): \[java] SwitchStmtsShouldHaveDefault false-positive with Java 14 switch non-fallthrough branches + * [#2883](https://github.com/pmd/pmd/issues/2883): \[java] JUnitAssertionsShouldIncludeMessage false positive with method call * java-codestyle * [#1673](https://github.com/pmd/pmd/issues/1673): \[java] UselessParentheses false positive with conditional operator * [#1790](https://github.com/pmd/pmd/issues/1790): \[java] UnnecessaryFullyQualifiedName false positive with enum constant * [#1918](https://github.com/pmd/pmd/issues/1918): \[java] UselessParentheses false positive with boolean operators * [#2299](https://github.com/pmd/pmd/issues/2299): \[java] UnnecessaryFullyQualifiedName false positive with similar package name * [#2739](https://github.com/pmd/pmd/issues/2739): \[java] UselessParentheses false positive for string concatenation +* java-errorprone + * [#1005](https://github.com/pmd/pmd/issues/1005): \[java] CloneMethodMustImplementCloneable triggers for interfaces + * [#2716](https://github.com/pmd/pmd/issues/2716): \[java] CompareObjectsWithEqualsRule: False positive with Enums + * [#2532](https://github.com/pmd/pmd/issues/2532): \[java] AvoidDecimalLiteralsInBigDecimalConstructor can not detect the case new BigDecimal(Expression) +* java-performance + * [#1224](https://github.com/pmd/pmd/issues/1224): \[java] InefficientEmptyStringCheck false negative in anonymous class + * [#2712](https://github.com/pmd/pmd/issues/2712): \[java] SimplifyStartsWith false-positive with AssertJ ### API Changes From 143153c7afcb069375c40abf24a08b2bef08e2b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 10 Nov 2020 12:06:34 +0100 Subject: [PATCH 163/299] Fix ordering --- docs/pages/7_0_0_release_notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index 5f20006891..dbb47d688f 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -116,8 +116,8 @@ The following previously deprecated rules have been finally removed: * [#2739](https://github.com/pmd/pmd/issues/2739): \[java] UselessParentheses false positive for string concatenation * java-errorprone * [#1005](https://github.com/pmd/pmd/issues/1005): \[java] CloneMethodMustImplementCloneable triggers for interfaces - * [#2716](https://github.com/pmd/pmd/issues/2716): \[java] CompareObjectsWithEqualsRule: False positive with Enums * [#2532](https://github.com/pmd/pmd/issues/2532): \[java] AvoidDecimalLiteralsInBigDecimalConstructor can not detect the case new BigDecimal(Expression) + * [#2716](https://github.com/pmd/pmd/issues/2716): \[java] CompareObjectsWithEqualsRule: False positive with Enums * java-performance * [#1224](https://github.com/pmd/pmd/issues/1224): \[java] InefficientEmptyStringCheck false negative in anonymous class * [#2712](https://github.com/pmd/pmd/issues/2712): \[java] SimplifyStartsWith false-positive with AssertJ From 8ffb948b40bbdf884d8a05d9c0d3cafd9e4bdbf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 10 Nov 2020 12:11:36 +0100 Subject: [PATCH 164/299] Fix #1212 Also fixes duplicate #2374 --- docs/pages/7_0_0_release_notes.md | 1 + .../JUnitTestContainsTooManyAssertsRule.java | 44 +++++++++++++++ .../JUnitTestsShouldIncludeAssertRule.java | 32 +---------- .../java/rule/internal/JUnitRuleUtil.java | 56 ++++++++++++++++++- .../resources/category/java/bestpractices.xml | 22 +------- .../xml/JUnitTestContainsTooManyAsserts.xml | 47 +++++++++++++++- 6 files changed, 149 insertions(+), 53 deletions(-) create mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index dbb47d688f..d0c7984d43 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -98,6 +98,7 @@ The following previously deprecated rules have been finally removed: * java-bestpractices * [#342](https://github.com/pmd/pmd/issues/342): \[java] AccessorMethodGeneration: Name clash with another public field not properly handled * [#807](https://github.com/pmd/pmd/issues/807): \[java] AccessorMethodGeneration false positive with overloads + * [#1212](https://github.com/pmd/pmd/issues/1212): \[java] Don't raise JUnitTestContainsTooManyAsserts on JUnit 5's assertAll * [#1422](https://github.com/pmd/pmd/issues/1422): \[java] JUnitTestsShouldIncludeAssert false positive with inherited @Rule field * [#1565](https://github.com/pmd/pmd/issues/1565): \[java] JUnitAssertionsShouldIncludeMessage false positive with AssertJ * [#1969](https://github.com/pmd/pmd/issues/1969): \[java] MissingOverride false-positive triggered by package-private method overwritten in another package by extending class diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java new file mode 100644 index 0000000000..4656549199 --- /dev/null +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java @@ -0,0 +1,44 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.bestpractices; + +import net.sourceforge.pmd.lang.java.ast.ASTBlock; +import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; +import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; +import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; +import net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil; +import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertyFactory; +import net.sourceforge.pmd.properties.constraints.NumericConstraints; + +public class JUnitTestContainsTooManyAssertsRule extends AbstractJavaRulechainRule { + + private static final PropertyDescriptor MAX_ASSERTS = + PropertyFactory.intProperty("maximumAsserts") + .desc("Maximum number of assert calls in a test method") + .require(NumericConstraints.positive()) + .defaultValue(1) + .build(); + + + public JUnitTestContainsTooManyAssertsRule() { + super(ASTMethodDeclaration.class); + definePropertyDescriptor(MAX_ASSERTS); + } + + @Override + public Object visit(ASTMethodDeclaration method, Object data) { + ASTBlock body = method.getBody(); + if (body != null && JUnitRuleUtil.isTestMethod(method)) { + int assertCount = body.descendants(ASTMethodCall.class) + .filter(JUnitRuleUtil::isProbableAssertCall) + .count(); + if (assertCount > getProperty(MAX_ASSERTS)) { + addViolation(data, method); + } + } + return data; + } +} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index 4daa9c7ad7..bfa3bd745a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -4,13 +4,11 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule { @@ -24,38 +22,12 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule ASTBlock body = method.getBody(); if (body != null && JUnitRuleUtil.isJUnitMethod(method) - && !isExpectAnnotated(method) + && !JUnitRuleUtil.isExpectAnnotated(method) && body.descendants(ASTMethodCall.class) - .none(JUnitTestsShouldIncludeAssertRule::isProbableAssertCall)) { + .none(JUnitRuleUtil::isProbableAssertCall)) { addViolation(data, method); } return data; } - private static boolean isProbableAssertCall(ASTMethodCall call) { - String name = call.getMethodName(); - return name.startsWith("assert") && !isSoftAssert(call) - || name.startsWith("check") - || name.startsWith("verify") - || "fail".equals(name) - || "failWith".equals(name) - || JUnitRuleUtil.isExpectExceptionCall(call); - } - - private static boolean isSoftAssert(ASTMethodCall call) { - return TypeTestUtil.isA("org.assertj.core.api.AbstractSoftAssertions", call.getMethodType().getDeclaringType()) - && !"assertAll".equals(call.getMethodName()); - } - - /** - * Tells if the node contains a Test annotation with an expected exception. - */ - private boolean isExpectAnnotated(ASTMethodDeclaration method) { - return method.getDeclaredAnnotations() - .filter(JUnitRuleUtil::isJunit4TestAnnotation) - .flatMap(ASTAnnotation::getMembers) - .any(it -> "expected".equals(it.getName())); - - } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java index 0412d5e286..bbbd94d08b 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java @@ -25,9 +25,21 @@ public final class JUnitRuleUtil { private static final String JUNIT3_CLASS_NAME = "junit.framework.TestCase"; private static final String JUNIT4_TEST_ANNOT = "org.junit.Test"; private static final String JUNIT5_TEST_ANNOT = "org.junit.jupiter.api.Test"; + + private static final String TESTNG_TEST_ANNOT = "org.testng.annotations.Test"; + + private static final Set JUNIT5_ALL_TEST_ANNOTS = + setOf("org.junit.jupiter.api.Test", + "org.junit.jupiter.api.RepeatedTest", + "org.junit.jupiter.api.TestFactory", + "org.junit.jupiter.api.TestTemplate", + "org.junit.jupiter.params.ParameterizedTest" + ); + private static final Set ASSERT_CONTAINERS = setOf("org.junit.Assert", "org.junit.jupiter.api.Assertions", "org.hamcrest.MatcherAssert", + "org.testng.Assert", "junit.framework.TestCase"); private JUnitRuleUtil() { @@ -49,12 +61,28 @@ public final class JUnitRuleUtil { return result; } + /** + * Returns true if this is either a JUnit test or a TestNG test. + */ + public static boolean isTestMethod(ASTMethodDeclaration method) { + return isJUnitMethod(method) || isTestNgMethod(method); + } + + private static boolean isTestNgMethod(ASTMethodDeclaration method) { + return method.isAnnotationPresent(TESTNG_TEST_ANNOT); + } + private static boolean isJUnit4Method(ASTMethodDeclaration method) { return method.isAnnotationPresent(JUNIT4_TEST_ANNOT) && method.isPublic(); } private static boolean isJUnit5Method(ASTMethodDeclaration method) { - return method.isAnnotationPresent(JUNIT5_TEST_ANNOT); + return method.getDeclaredAnnotations().any( + it -> { + String canonicalName = it.getTypeMirror().getSymbol().getCanonicalName(); + return JUNIT5_ALL_TEST_ANNOTS.contains(canonicalName); + } + ); } private static boolean isJUnit3Method(ASTMethodDeclaration method) { @@ -99,4 +127,30 @@ public final class JUnitRuleUtil { String binaryName = !(sym instanceof JClassSymbol) ? null : ((JClassSymbol) sym).getBinaryName(); return qualifierTypes.contains(binaryName); } + + public static boolean isProbableAssertCall(ASTMethodCall call) { + String name = call.getMethodName(); + return name.startsWith("assert") && !isSoftAssert(call) + || name.startsWith("check") + || name.startsWith("verify") + || "fail".equals(name) + || "failWith".equals(name) + || isExpectExceptionCall(call); + } + + private static boolean isSoftAssert(ASTMethodCall call) { + return TypeTestUtil.isA("org.assertj.core.api.AbstractSoftAssertions", call.getMethodType().getDeclaringType()) + && !"assertAll".equals(call.getMethodName()); + } + + /** + * Tells if the node contains a @Test annotation with an expected exception. + */ + public static boolean isExpectAnnotated(ASTMethodDeclaration method) { + return method.getDeclaredAnnotations() + .filter(JUnitRuleUtil::isJunit4TestAnnotation) + .flatMap(ASTAnnotation::getMembers) + .any(it -> "expected".equals(it.getName())); + + } } diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index e3e00a93a3..77bbfecfb4 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -794,7 +794,7 @@ public class Foo extends TestCase { language="java" since="5.0" message="Unit tests should not contain more than ${maximumAsserts} assert(s)." - class="net.sourceforge.pmd.lang.rule.XPathRule" + class="net.sourceforge.pmd.lang.java.rule.bestpractices.JUnitTestContainsTooManyAssertsRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#junittestcontainstoomanyasserts"> Unit tests should not contain too many asserts. Many asserts are indicative of a complex test, for which @@ -804,26 +804,6 @@ Customize the maximum number of assertions used by this Rule to suit your needs. This rule checks for JUnit4, JUnit5 and TestNG Tests, as well as methods starting with "test". 3 - - - - - $maximumAsserts]] -]]> - - - JUnit 3 Test contains more than one assert 1 JUnit 5 Test contains more than one assert 5 - 10,17,24,31,39 + 11,18,25,32,40 + + #1212 [java] Don't raise JUnitTestContainsTooManyAsserts on JUnit 5's assertAll + 0 + assertEquals(expResult, result), + () -> assertEquals(3, mutableInteger.getValue())); + } + } + ]]> + + + #2374 [java] False positive JUnitTestContainsTooManyAsserts for JUnit 5 assertAll() + 0 + assertEquals(2, result.x), + () -> assertEquals(4, result.y) + ); + } + } + ]]> + From 5ac0c9f9d9b314cda9cf04c8348cd9ec876d186f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 10 Nov 2020 12:41:07 +0100 Subject: [PATCH 165/299] Check fixes #2538 --- docs/pages/7_0_0_release_notes.md | 2 ++ .../java/rule/multithreading/xml/DontCallThreadRun.xml | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index d0c7984d43..ab207c2624 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -119,6 +119,8 @@ The following previously deprecated rules have been finally removed: * [#1005](https://github.com/pmd/pmd/issues/1005): \[java] CloneMethodMustImplementCloneable triggers for interfaces * [#2532](https://github.com/pmd/pmd/issues/2532): \[java] AvoidDecimalLiteralsInBigDecimalConstructor can not detect the case new BigDecimal(Expression) * [#2716](https://github.com/pmd/pmd/issues/2716): \[java] CompareObjectsWithEqualsRule: False positive with Enums +* java-multithreading + * [#2538](https://github.com/pmd/pmd/issues/2538): \[java] DontCallThreadRun can't detect the case that call run() in `foo.bar.run()` * java-performance * [#1224](https://github.com/pmd/pmd/issues/1224): \[java] InefficientEmptyStringCheck false negative in anonymous class * [#2712](https://github.com/pmd/pmd/issues/2712): \[java] SimplifyStartsWith false-positive with AssertJ diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DontCallThreadRun.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DontCallThreadRun.xml index b5609a55df..34b63ef4ea 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DontCallThreadRun.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DontCallThreadRun.xml @@ -30,14 +30,14 @@ public class Foo { - #565 - inheritance use case - call to Thread().run() + [java]DontCallThreadRun can't detect the case that call run() in foo.bar.run() #2538 1 From 782acd55ad3667f52f7e3a690a496c21b2bf1736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 10 Nov 2020 12:44:44 +0100 Subject: [PATCH 166/299] Check fixes #2537 --- docs/pages/7_0_0_release_notes.md | 1 + .../rule/multithreading/xml/DontCallThreadRun.xml | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index ab207c2624..002de069b1 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -120,6 +120,7 @@ The following previously deprecated rules have been finally removed: * [#2532](https://github.com/pmd/pmd/issues/2532): \[java] AvoidDecimalLiteralsInBigDecimalConstructor can not detect the case new BigDecimal(Expression) * [#2716](https://github.com/pmd/pmd/issues/2716): \[java] CompareObjectsWithEqualsRule: False positive with Enums * java-multithreading + * [#2537](https://github.com/pmd/pmd/issues/2537): \[java] DontCallThreadRun can't detect the case that call run() in `this.run()` * [#2538](https://github.com/pmd/pmd/issues/2538): \[java] DontCallThreadRun can't detect the case that call run() in `foo.bar.run()` * java-performance * [#1224](https://github.com/pmd/pmd/issues/1224): \[java] InefficientEmptyStringCheck false negative in anonymous class diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DontCallThreadRun.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DontCallThreadRun.xml index 34b63ef4ea..9030c6367f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DontCallThreadRun.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/DontCallThreadRun.xml @@ -42,4 +42,18 @@ public class Foo { } ]]> + + [java]DontCallThreadRun can't detect the case that call run() in this.run() #2537 + 1 + + From bf7911468263f30122326b3aff6cbdcec22c4d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 10 Nov 2020 12:50:06 +0100 Subject: [PATCH 167/299] Check fixes #2880 --- docs/pages/7_0_0_release_notes.md | 1 + .../xml/CompareObjectsWithEquals.xml | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index 002de069b1..6eb65fca6d 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -119,6 +119,7 @@ The following previously deprecated rules have been finally removed: * [#1005](https://github.com/pmd/pmd/issues/1005): \[java] CloneMethodMustImplementCloneable triggers for interfaces * [#2532](https://github.com/pmd/pmd/issues/2532): \[java] AvoidDecimalLiteralsInBigDecimalConstructor can not detect the case new BigDecimal(Expression) * [#2716](https://github.com/pmd/pmd/issues/2716): \[java] CompareObjectsWithEqualsRule: False positive with Enums + * [#2880](https://github.com/pmd/pmd/issues/2880): \[java] CompareObjectsWithEquals - false negative with type res * java-multithreading * [#2537](https://github.com/pmd/pmd/issues/2537): \[java] DontCallThreadRun can't detect the case that call run() in `this.run()` * [#2538](https://github.com/pmd/pmd/issues/2538): \[java] DontCallThreadRun can't detect the case that call run() in `foo.bar.run()` diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index 9b8e55f9c5..9a1cd42751 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -247,4 +247,26 @@ public class EnumTest { } ]]> + + [java] CompareObjectsWithEquals - false negative with type res #2880 + 2 + + From 3a1940d5a0fd36783ecc3dcc67c92c03a26d96cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 10 Nov 2020 14:11:34 +0100 Subject: [PATCH 168/299] Fix pmd warnings --- .../JUnitAssertionsShouldIncludeMessageRule.java | 4 ++-- .../JUnitTestContainsTooManyAssertsRule.java | 6 +++--- .../bestpractices/JUnitTestsShouldIncludeAssertRule.java | 8 ++++---- .../java/rule/bestpractices/JUnitUseExpectedRule.java | 2 +- .../pmd/lang/java/rule/errorprone/JUnitSpellingRule.java | 4 ++-- .../lang/java/rule/errorprone/JUnitStaticSuiteRule.java | 2 +- .../rule/errorprone/TestClassWithoutTestCasesRule.java | 6 +++--- .../{JUnitRuleUtil.java => TestFrameworksUtil.java} | 9 ++++----- 8 files changed, 20 insertions(+), 21 deletions(-) rename pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/{JUnitRuleUtil.java => TestFrameworksUtil.java} (95%) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java index 329021d010..1148593bb0 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitAssertionsShouldIncludeMessageRule.java @@ -10,7 +10,7 @@ import java.util.List; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -import net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil; +import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; import net.sourceforge.pmd.lang.java.types.TypeTestUtil.InvocationMatcher; public class JUnitAssertionsShouldIncludeMessageRule extends AbstractJavaRulechainRule { @@ -37,7 +37,7 @@ public class JUnitAssertionsShouldIncludeMessageRule extends AbstractJavaRulecha @Override public Object visit(ASTMethodCall node, Object data) { - if (JUnitRuleUtil.isCallOnAssertionContainer(node)) { + if (TestFrameworksUtil.isCallOnAssertionContainer(node)) { for (InvocationMatcher check : checks) { if (check.matchesCall(node)) { addViolation(data, node); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java index 4656549199..33d578d98a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestContainsTooManyAssertsRule.java @@ -8,7 +8,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -import net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil; +import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; import net.sourceforge.pmd.properties.constraints.NumericConstraints; @@ -31,9 +31,9 @@ public class JUnitTestContainsTooManyAssertsRule extends AbstractJavaRulechainRu @Override public Object visit(ASTMethodDeclaration method, Object data) { ASTBlock body = method.getBody(); - if (body != null && JUnitRuleUtil.isTestMethod(method)) { + if (body != null && TestFrameworksUtil.isTestMethod(method)) { int assertCount = body.descendants(ASTMethodCall.class) - .filter(JUnitRuleUtil::isProbableAssertCall) + .filter(TestFrameworksUtil::isProbableAssertCall) .count(); if (assertCount > getProperty(MAX_ASSERTS)) { addViolation(data, method); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java index bfa3bd745a..b00a712ff4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitTestsShouldIncludeAssertRule.java @@ -8,7 +8,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -import net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil; +import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule { @@ -21,10 +21,10 @@ public class JUnitTestsShouldIncludeAssertRule extends AbstractJavaRulechainRule public Object visit(ASTMethodDeclaration method, Object data) { ASTBlock body = method.getBody(); if (body != null - && JUnitRuleUtil.isJUnitMethod(method) - && !JUnitRuleUtil.isExpectAnnotated(method) + && TestFrameworksUtil.isJUnitMethod(method) + && !TestFrameworksUtil.isExpectAnnotated(method) && body.descendants(ASTMethodCall.class) - .none(JUnitRuleUtil::isProbableAssertCall)) { + .none(TestFrameworksUtil::isProbableAssertCall)) { addViolation(data, method); } return data; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java index 363e6ff02c..b62bffb77f 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/JUnitUseExpectedRule.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.java.rule.bestpractices; -import static net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil.isJUnitMethod; +import static net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil.isJUnitMethod; import net.sourceforge.pmd.lang.java.ast.ASTBlock; import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java index 446449f9c5..f12a937083 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitSpellingRule.java @@ -8,7 +8,7 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -import net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil; +import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; public class JUnitSpellingRule extends AbstractJavaRulechainRule { @@ -18,7 +18,7 @@ public class JUnitSpellingRule extends AbstractJavaRulechainRule { @Override public Object visit(ASTClassOrInterfaceDeclaration node, Object data) { - if (JUnitRuleUtil.isJUnit3Class(node)) { + if (TestFrameworksUtil.isJUnit3Class(node)) { node.getDeclarations(ASTMethodDeclaration.class) .filter(this::isViolation) .forEach(it -> addViolation(data, it)); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java index a013557e03..a95edc6abe 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/JUnitStaticSuiteRule.java @@ -5,7 +5,7 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import static net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil.isJUnit3Class; +import static net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil.isJUnit3Class; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java index b06c978421..145876c7fe 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/TestClassWithoutTestCasesRule.java @@ -4,12 +4,12 @@ package net.sourceforge.pmd.lang.java.rule.errorprone; -import static net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil.isJUnit3Class; +import static net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil.isJUnit3Class; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -import net.sourceforge.pmd.lang.java.rule.internal.JUnitRuleUtil; +import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; public class TestClassWithoutTestCasesRule extends AbstractJavaRulechainRule { @@ -22,7 +22,7 @@ public class TestClassWithoutTestCasesRule extends AbstractJavaRulechainRule { if (isJUnit3Class(node)) { boolean hasTests = node.getDeclarations(ASTMethodDeclaration.class) - .any(JUnitRuleUtil::isJunit3MethodSignature); + .any(TestFrameworksUtil::isJunit3MethodSignature); if (!hasTests) { addViolation(data, node); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/TestFrameworksUtil.java similarity index 95% rename from pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java rename to pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/TestFrameworksUtil.java index bbbd94d08b..48b04d90e9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/JUnitRuleUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/internal/TestFrameworksUtil.java @@ -18,13 +18,12 @@ import net.sourceforge.pmd.lang.java.types.JTypeMirror; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; /** - * Utilities for junit-related rules. + * Utilities for rules related to test frameworks (Junit, TestNG, etc). */ -public final class JUnitRuleUtil { +public final class TestFrameworksUtil { private static final String JUNIT3_CLASS_NAME = "junit.framework.TestCase"; private static final String JUNIT4_TEST_ANNOT = "org.junit.Test"; - private static final String JUNIT5_TEST_ANNOT = "org.junit.jupiter.api.Test"; private static final String TESTNG_TEST_ANNOT = "org.testng.annotations.Test"; @@ -42,7 +41,7 @@ public final class JUnitRuleUtil { "org.testng.Assert", "junit.framework.TestCase"); - private JUnitRuleUtil() { + private TestFrameworksUtil() { // utility class } @@ -148,7 +147,7 @@ public final class JUnitRuleUtil { */ public static boolean isExpectAnnotated(ASTMethodDeclaration method) { return method.getDeclaredAnnotations() - .filter(JUnitRuleUtil::isJunit4TestAnnotation) + .filter(TestFrameworksUtil::isJunit4TestAnnotation) .flatMap(ASTAnnotation::getMembers) .any(it -> "expected".equals(it.getName())); From d571885771f6631f68ade380a938093ec17a66d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 11 Nov 2020 14:59:02 +0100 Subject: [PATCH 169/299] Check fixes #2577 --- docs/pages/7_0_0_release_notes.md | 1 + .../xml/UseNotifyAllInsteadOfNotify.xml | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index 6eb65fca6d..aa9a1e8721 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -123,6 +123,7 @@ The following previously deprecated rules have been finally removed: * java-multithreading * [#2537](https://github.com/pmd/pmd/issues/2537): \[java] DontCallThreadRun can't detect the case that call run() in `this.run()` * [#2538](https://github.com/pmd/pmd/issues/2538): \[java] DontCallThreadRun can't detect the case that call run() in `foo.bar.run()` + * [#2577](https://github.com/pmd/pmd/issues/2577): \[java] UseNotifyAllInsteadOfNotify falsely detect a special case with argument: `foo.notify(bar)` * java-performance * [#1224](https://github.com/pmd/pmd/issues/1224): \[java] InefficientEmptyStringCheck false negative in anonymous class * [#2712](https://github.com/pmd/pmd/issues/2712): \[java] SimplifyStartsWith false-positive with AssertJ diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseNotifyAllInsteadOfNotify.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseNotifyAllInsteadOfNotify.xml index e17d7ed51a..a5c7622583 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseNotifyAllInsteadOfNotify.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/multithreading/xml/UseNotifyAllInsteadOfNotify.xml @@ -89,6 +89,18 @@ public class FlasePositive { boolean notify; notify = true; } +} + ]]> + + + [java]UseNotifyAllInsteadOfNotify falsely detect a special case with argument: foo.notify(bar) #2577 + 0 + From 423949cc276df6c6eaf312ef4ffbcb977fe258b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 11 Nov 2020 15:40:17 +0100 Subject: [PATCH 170/299] Fix #2528 --- docs/pages/7_0_0_release_notes.md | 1 + .../MethodNamingConventionsRule.java | 45 +++---------------- .../rule/internal/TestFrameworksUtil.java | 6 +-- .../codestyle/xml/MethodNamingConventions.xml | 28 ++++++++++++ 4 files changed, 39 insertions(+), 41 deletions(-) diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index aa9a1e8721..4d623b3e5c 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -114,6 +114,7 @@ The following previously deprecated rules have been finally removed: * [#1790](https://github.com/pmd/pmd/issues/1790): \[java] UnnecessaryFullyQualifiedName false positive with enum constant * [#1918](https://github.com/pmd/pmd/issues/1918): \[java] UselessParentheses false positive with boolean operators * [#2299](https://github.com/pmd/pmd/issues/2299): \[java] UnnecessaryFullyQualifiedName false positive with similar package name + * [#2528](https://github.com/pmd/pmd/issues/2528): \[java] MethodNamingConventions - JUnit 5 method naming not support ParameterizedTest * [#2739](https://github.com/pmd/pmd/issues/2739): \[java] UselessParentheses false positive for string concatenation * java-errorprone * [#1005](https://github.com/pmd/pmd/issues/1005): \[java] CloneMethodMustImplementCloneable triggers for interfaces 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 ca5c7c623c..b25e691c11 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 @@ -4,19 +4,13 @@ package net.sourceforge.pmd.lang.java.rule.codestyle; -import static net.sourceforge.pmd.lang.ast.NodeStream.asInstanceOf; - import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTEnumConstant; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.JavaNode; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; +import net.sourceforge.pmd.lang.java.ast.JModifier; +import net.sourceforge.pmd.lang.java.rule.internal.TestFrameworksUtil; import net.sourceforge.pmd.properties.PropertyBuilder.RegexPropertyBuilder; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -43,47 +37,22 @@ public class MethodNamingConventionsRule extends AbstractNamingConventionRule { String canonicalName = it.getTypeMirror().getSymbol().getCanonicalName(); @@ -84,7 +84,7 @@ public final class TestFrameworksUtil { ); } - private static boolean isJUnit3Method(ASTMethodDeclaration method) { + public static boolean isJUnit3Method(ASTMethodDeclaration method) { return TypeTestUtil.isA("junit.framework.TestCase", method.getEnclosingType()) && isJunit3MethodSignature(method); } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml index 2326457a5e..c727224ba2 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/MethodNamingConventions.xml @@ -223,6 +223,34 @@ public class Foo { public void m_fooBar() { } +} + ]]> + + + #2528 [java] MethodNamingConventions - JUnit 5 method naming not support ParameterizedTest + moo_[a-z][A-Za-z]* + 1 + 14 + + The JUnit 5 test method name 'moo_str2' doesn't match 'moo_[a-z][A-Za-z]*' + + From 8ac13b769ed0b2601fa2f580e017b077e1ffacb9 Mon Sep 17 00:00:00 2001 From: Gunther Schrijvers Date: Thu, 12 Nov 2020 11:41:10 +0100 Subject: [PATCH 171/299] [core] Modified test cases for TextRenderer to include the RuleName in the generated output --- .../src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java | 2 +- .../net/sourceforge/pmd/renderers/TextRendererTest.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) 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 396bda7876..6e865fd888 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 @@ -78,7 +78,7 @@ public class PMDTaskTest { String actual = IOUtils.toString(in, StandardCharsets.UTF_8); // remove any trailing newline actual = actual.replaceAll("\n|\r", ""); - Assert.assertEquals("sample.dummy:0:\tTest Rule 2", actual); + Assert.assertEquals("sample.dummy:0:\tTest Rule 2:\tSampleXPathRule", actual); } } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java index e5118ab038..57e560818e 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java @@ -17,7 +17,7 @@ public class TextRendererTest extends AbstractRendererTest { @Override public String getExpected() { - return getSourceCodeFilename() + ":1:\tblah" + PMD.EOL; + return getSourceCodeFilename() + ":1:\tblah:\tFoo" + PMD.EOL; } @Override @@ -27,8 +27,8 @@ public class TextRendererTest extends AbstractRendererTest { @Override public String getExpectedMultiple() { - return getSourceCodeFilename() + ":1:\tblah" + PMD.EOL - + getSourceCodeFilename() + ":1:\tblah" + PMD.EOL; + return getSourceCodeFilename() + ":1:\tblah:\tFoo" + PMD.EOL + + getSourceCodeFilename() + ":1:\tblah:\tFoo" + PMD.EOL; } @Override From 3a2c1b8334d9df24e0a7e357f79f2067107c95ba Mon Sep 17 00:00:00 2001 From: Gunther Schrijvers Date: Thu, 12 Nov 2020 11:44:18 +0100 Subject: [PATCH 172/299] [core] Modified output of TextRenderer to include the RuleName in the generated output --- .../main/java/net/sourceforge/pmd/renderers/TextRenderer.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index 6830c97b7c..448364bf6b 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -36,7 +36,8 @@ public class TextRenderer extends AbstractIncrementingRenderer { RuleViolation rv = violations.next(); buf.append(determineFileName(rv.getFilename())); buf.append(':').append(Integer.toString(rv.getBeginLine())); - buf.append(":\t").append(rv.getDescription()).append(PMD.EOL); + buf.append(":\t").append(rv.getDescription()); + buf.append(":\t").append(rv.getRule().getName()).append(PMD.EOL); writer.write(buf.toString()); } } From 72291e9a59ea9216a308051832b5a2c111943a86 Mon Sep 17 00:00:00 2001 From: Gunther Schrijvers Date: Thu, 12 Nov 2020 11:46:05 +0100 Subject: [PATCH 173/299] [core] Removed unnecessary explicit casting of Integer to String --- .../main/java/net/sourceforge/pmd/renderers/TextRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index 448364bf6b..4b44c700ed 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -35,7 +35,7 @@ public class TextRenderer extends AbstractIncrementingRenderer { buf.setLength(0); RuleViolation rv = violations.next(); buf.append(determineFileName(rv.getFilename())); - buf.append(':').append(Integer.toString(rv.getBeginLine())); + buf.append(':').append(rv.getBeginLine()); buf.append(":\t").append(rv.getDescription()); buf.append(":\t").append(rv.getRule().getName()).append(PMD.EOL); writer.write(buf.toString()); From 7ef6084a79c8a43bc0cccfb4077f90e11a234f4f Mon Sep 17 00:00:00 2001 From: Gunther Schrijvers Date: Thu, 12 Nov 2020 11:57:01 +0100 Subject: [PATCH 174/299] [core] Refactored generation of Text output to use constants defining the 3 different types of text seperators: small '-'; medium ':\t' and large '\t-\t' to increase readability and to standardise the text output. --- .../sourceforge/pmd/renderers/TextRenderer.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index 4b44c700ed..dddc4c2440 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -16,6 +16,10 @@ import net.sourceforge.pmd.RuleViolation; */ public class TextRenderer extends AbstractIncrementingRenderer { + private static final Character SMALL_SEPERATOR = ':'; + private static final String MEDIUM_SEPERATOR = ":\t"; + private static final String LARGE_SEPERATOR = "\t-\t"; + public static final String NAME = "text"; public TextRenderer() { @@ -35,9 +39,9 @@ public class TextRenderer extends AbstractIncrementingRenderer { buf.setLength(0); RuleViolation rv = violations.next(); buf.append(determineFileName(rv.getFilename())); - buf.append(':').append(rv.getBeginLine()); - buf.append(":\t").append(rv.getDescription()); - buf.append(":\t").append(rv.getRule().getName()).append(PMD.EOL); + buf.append(SMALL_SEPERATOR).append(rv.getBeginLine()); + buf.append(MEDIUM_SEPERATOR).append(rv.getDescription()); + buf.append(MEDIUM_SEPERATOR).append(rv.getRule().getName()).append(PMD.EOL); writer.write(buf.toString()); } } @@ -49,7 +53,7 @@ public class TextRenderer extends AbstractIncrementingRenderer { for (Report.ProcessingError error : errors) { buf.setLength(0); buf.append(determineFileName(error.getFile())); - buf.append("\t-\t").append(error.getMsg()).append(PMD.EOL); + buf.append(LARGE_SEPERATOR).append(error.getMsg()).append(PMD.EOL); writer.write(buf.toString()); } @@ -65,7 +69,7 @@ public class TextRenderer extends AbstractIncrementingRenderer { for (Report.ConfigurationError error : configErrors) { buf.setLength(0); buf.append(error.rule().getName()); - buf.append("\t-\t").append(error.issue()).append(PMD.EOL); + buf.append(LARGE_SEPERATOR).append(error.issue()).append(PMD.EOL); writer.write(buf.toString()); } } From c7022c4881a206e8b914a44eeb4287972a6b5955 Mon Sep 17 00:00:00 2001 From: Gunther Schrijvers Date: Thu, 12 Nov 2020 13:05:03 +0100 Subject: [PATCH 175/299] [core] Corrected constant naming from SEPERATOR to SEPARATOR. --- .../sourceforge/pmd/renderers/TextRenderer.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index dddc4c2440..043a3c6707 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -16,9 +16,9 @@ import net.sourceforge.pmd.RuleViolation; */ public class TextRenderer extends AbstractIncrementingRenderer { - private static final Character SMALL_SEPERATOR = ':'; - private static final String MEDIUM_SEPERATOR = ":\t"; - private static final String LARGE_SEPERATOR = "\t-\t"; + private static final Character SMALL_SEPARATOR = ':'; + private static final String MEDIUM_SEPARATOR = ":\t"; + private static final String LARGE_SEPARATOR = "\t-\t"; public static final String NAME = "text"; @@ -39,9 +39,9 @@ public class TextRenderer extends AbstractIncrementingRenderer { buf.setLength(0); RuleViolation rv = violations.next(); buf.append(determineFileName(rv.getFilename())); - buf.append(SMALL_SEPERATOR).append(rv.getBeginLine()); - buf.append(MEDIUM_SEPERATOR).append(rv.getDescription()); - buf.append(MEDIUM_SEPERATOR).append(rv.getRule().getName()).append(PMD.EOL); + buf.append(SMALL_SEPARATOR).append(rv.getBeginLine()); + buf.append(MEDIUM_SEPARATOR).append(rv.getDescription()); + buf.append(MEDIUM_SEPARATOR).append(rv.getRule().getName()).append(PMD.EOL); writer.write(buf.toString()); } } @@ -53,7 +53,7 @@ public class TextRenderer extends AbstractIncrementingRenderer { for (Report.ProcessingError error : errors) { buf.setLength(0); buf.append(determineFileName(error.getFile())); - buf.append(LARGE_SEPERATOR).append(error.getMsg()).append(PMD.EOL); + buf.append(LARGE_SEPARATOR).append(error.getMsg()).append(PMD.EOL); writer.write(buf.toString()); } @@ -69,7 +69,7 @@ public class TextRenderer extends AbstractIncrementingRenderer { for (Report.ConfigurationError error : configErrors) { buf.setLength(0); buf.append(error.rule().getName()); - buf.append(LARGE_SEPERATOR).append(error.issue()).append(PMD.EOL); + buf.append(LARGE_SEPARATOR).append(error.issue()).append(PMD.EOL); writer.write(buf.toString()); } } From fbe2e6bb2e33072c197df5a154e160a8743c594c Mon Sep 17 00:00:00 2001 From: Gunther Schrijvers Date: Thu, 12 Nov 2020 13:13:08 +0100 Subject: [PATCH 176/299] [core] Changed output for a violating rule to put the RuleName in front of the description after receiving feedback on the PR. --- .../java/net/sourceforge/pmd/renderers/TextRenderer.java | 4 ++-- .../src/test/java/net/sourceforge/pmd/ant/PMDTaskTest.java | 2 +- .../net/sourceforge/pmd/renderers/TextRendererTest.java | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index 043a3c6707..f66fd971bb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -40,8 +40,8 @@ public class TextRenderer extends AbstractIncrementingRenderer { RuleViolation rv = violations.next(); buf.append(determineFileName(rv.getFilename())); buf.append(SMALL_SEPARATOR).append(rv.getBeginLine()); - buf.append(MEDIUM_SEPARATOR).append(rv.getDescription()); - buf.append(MEDIUM_SEPARATOR).append(rv.getRule().getName()).append(PMD.EOL); + buf.append(MEDIUM_SEPARATOR).append(rv.getRule().getName()); + buf.append(MEDIUM_SEPARATOR).append(rv.getDescription()).append(PMD.EOL); writer.write(buf.toString()); } } 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 6e865fd888..0bdf85b5ad 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 @@ -78,7 +78,7 @@ public class PMDTaskTest { String actual = IOUtils.toString(in, StandardCharsets.UTF_8); // remove any trailing newline actual = actual.replaceAll("\n|\r", ""); - Assert.assertEquals("sample.dummy:0:\tTest Rule 2:\tSampleXPathRule", actual); + Assert.assertEquals("sample.dummy:0:\tSampleXPathRule:\tTest Rule 2", actual); } } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java index 57e560818e..69983b5f85 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/TextRendererTest.java @@ -17,7 +17,7 @@ public class TextRendererTest extends AbstractRendererTest { @Override public String getExpected() { - return getSourceCodeFilename() + ":1:\tblah:\tFoo" + PMD.EOL; + return getSourceCodeFilename() + ":1:\tFoo:\tblah" + PMD.EOL; } @Override @@ -27,8 +27,8 @@ public class TextRendererTest extends AbstractRendererTest { @Override public String getExpectedMultiple() { - return getSourceCodeFilename() + ":1:\tblah:\tFoo" + PMD.EOL - + getSourceCodeFilename() + ":1:\tblah:\tFoo" + PMD.EOL; + return getSourceCodeFilename() + ":1:\tFoo:\tblah" + PMD.EOL + + getSourceCodeFilename() + ":1:\tFoo:\tblah" + PMD.EOL; } @Override From e4ecc3bfb78e13cc7018c95bec656f84499ee8d8 Mon Sep 17 00:00:00 2001 From: GuntherSchrijvers <56870283+GuntherSchrijvers@users.noreply.github.com> Date: Thu, 12 Nov 2020 14:24:05 +0100 Subject: [PATCH 177/299] Update pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Clément Fournier --- .../main/java/net/sourceforge/pmd/renderers/TextRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java index f66fd971bb..afce5e0740 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/TextRenderer.java @@ -16,7 +16,7 @@ import net.sourceforge.pmd.RuleViolation; */ public class TextRenderer extends AbstractIncrementingRenderer { - private static final Character SMALL_SEPARATOR = ':'; + private static final char SMALL_SEPARATOR = ':'; private static final String MEDIUM_SEPARATOR = ":\t"; private static final String LARGE_SEPARATOR = "\t-\t"; From db7bf0a786954ee065a1ac0f4959cd9ac13cfbc7 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Thu, 12 Nov 2020 10:15:04 -0800 Subject: [PATCH 178/299] 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 179/299] 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 d182067fb7906ff06b16befbdc7526b8eefbf2d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Nov 2020 12:47:01 +0100 Subject: [PATCH 180/299] Test xpath functions WIP --- .../xpath/internal/BaseXPathFunctionTest.java | 69 ++++++++++++++ .../internal/XPathMetricFunctionTest.java | 95 +++---------------- .../pmd/lang/ast/test/BaseParsingHelper.kt | 3 +- 3 files changed, 85 insertions(+), 82 deletions(-) create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java new file mode 100644 index 0000000000..dd41f4315a --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java @@ -0,0 +1,69 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.xpath.internal; + +import java.util.function.Consumer; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.junit.Assert; + +import net.sourceforge.pmd.Report; +import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.ast.test.TestUtilsKt; +import net.sourceforge.pmd.lang.java.JavaLanguageModule; +import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest; +import net.sourceforge.pmd.lang.rule.XPathRule; +import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; + +/** + * @author Clément Fournier + * @since 6.0.0 + */ +public class BaseXPathFunctionTest extends BaseNonParserTest { + + private static final String VIOLATION_MESSAGE = "violation"; + + + private @NonNull Report executeRule(Rule rule, String code) { + return java.executeRule(rule, code); + } + + protected Rule makeXpathRuleFromXPath(String xpath) { + XPathRule rule = new XPathRule(XPathVersion.DEFAULT, xpath); + rule.setMessage(VIOLATION_MESSAGE); + rule.setLanguage(LanguageRegistry.getLanguage(JavaLanguageModule.NAME)); + return rule; + } + + protected void assertReportSize(Rule rule, String code, int numViolations) { + Report report = executeRule(rule, code); + TestUtilsKt.assertSize(report, numViolations); + } + + + protected void testWithExpectedException(String xpath, + String code, + Class exceptionClass, + Consumer exceptionSpec) { + + Rule rule = makeXpathRuleFromXPath(xpath); + + E thrown = Assert.assertThrows(exceptionClass, () -> executeRule(rule, code)); + + exceptionSpec.accept(thrown); + } + + + protected void testWithExpectedException(String xpath, + String code, + Class exceptionClass, + String expectMessage) { + + testWithExpectedException(xpath, code, exceptionClass, thrown -> Assert.assertEquals("Wrong message", expectMessage, thrown.getMessage())); + } + + +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/XPathMetricFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/XPathMetricFunctionTest.java index 9ac87e229b..9017c4a5e9 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/XPathMetricFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/XPathMetricFunctionTest.java @@ -4,94 +4,45 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.io.StringReader; -import java.util.Iterator; - import org.junit.Test; -import org.junit.rules.ExpectedException; -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.java.JavaLanguageModule; -import net.sourceforge.pmd.lang.rule.XPathRule; -import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; /** * @author Clément Fournier * @since 6.0.0 */ -public class XPathMetricFunctionTest { - - private static final String VIOLATION_MESSAGE = "violation"; - - @org.junit.Rule - public ExpectedException expected = ExpectedException.none(); - - - private Rule makeXpathRuleFromXPath(String xpath) { - XPathRule rule = new XPathRule(XPathVersion.XPATH_1_0, xpath); - rule.setMessage(VIOLATION_MESSAGE); - rule.setLanguage(LanguageRegistry.getLanguage(JavaLanguageModule.NAME)); - return rule; - } - - - private Iterator getViolations(Rule rule, String code) throws PMDException { - PMD p = new PMD(); - RuleContext ctx = new RuleContext(); - Report report = new Report(); - 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); - p.getSourceCodeProcessor().processSourceCode(new StringReader(code), new RuleSets(rules), ctx); - return report.getViolations().iterator(); - } - +public class XPathMetricFunctionTest extends BaseXPathFunctionTest { @Test - public void testWellFormedClassMetricRule() throws PMDException { + public void testWellFormedClassMetricRule() { Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceDeclaration[pmd-java:metric('NCSS') > 0]"); - final String code = "class Foo { Foo() {} void bar() {}}"; + String code = "class Foo { Foo() {} void bar() {}}"; - Iterator violations = getViolations(rule, code); - assertTrue(violations.hasNext()); + assertReportSize(rule, code, 1); } @Test - public void testWellFormedOperationMetricRule() throws PMDException { + public void testWellFormedOperationMetricRule() { Rule rule = makeXpathRuleFromXPath("//ConstructorDeclaration[pmd-java:metric('CYCLO') > 1]"); - final String code = "class Goo { Goo() {if(true){}} }"; + String code = "class Goo { Goo() {if(true){}} }"; - Iterator violations = getViolations(rule, code); - assertTrue(violations.hasNext()); + assertReportSize(rule, code, 1); } @Test - public void testBadCase() throws PMDException { + public void testBadCase() { Rule rule = makeXpathRuleFromXPath("//ConstructorDeclaration[pmd-java:metric('cYclo') > 1]"); - final String code = "class Hoo { Hoo() {if(true){}} }"; + String code = "class Hoo { Hoo() {if(true){}} }"; - Iterator violations = getViolations(rule, code); - assertTrue(violations.hasNext()); + assertReportSize(rule, code, 1); } @Test - public void testNonexistentMetric() throws Exception { + public void testNonexistentMetric() { testWithExpectedException("//ConstructorDeclaration[pmd-java:metric('FOOBAR') > 1]", "class Joo { Joo() {if(true){}} }", IllegalArgumentException.class, @@ -100,7 +51,7 @@ public class XPathMetricFunctionTest { @Test - public void testWrongNodeTypeGeneric() throws Exception { + public void testWrongNodeTypeGeneric() { testWithExpectedException("//IfStatement[pmd-java:metric('NCSS') > 1]", "class Koo { Koo() {if(true){}} }", IllegalStateException.class, @@ -109,7 +60,7 @@ public class XPathMetricFunctionTest { @Test - public void testWrongMetricKeyForTypeDeclaration() throws Exception { + public void testWrongMetricKeyForTypeDeclaration() { testWithExpectedException("//EnumDeclaration[pmd-java:metric('CYCLO') > 1]", "enum Loo { FOO; }", IllegalArgumentException.class, @@ -118,7 +69,7 @@ public class XPathMetricFunctionTest { @Test - public void testWrongMetricKeyForOperationDeclaration() throws Exception { + public void testWrongMetricKeyForOperationDeclaration() { testWithExpectedException("//MethodDeclaration[pmd-java:metric('WMC') > 1]", "class Moo { void foo() {if(true){}} }", IllegalArgumentException.class, @@ -126,22 +77,4 @@ public class XPathMetricFunctionTest { } - private void testWithExpectedException(String xpath, String code, - Class expectedThrowable, - String expectedMessage) throws Exception { - - Rule rule = makeXpathRuleFromXPath(xpath); - - expected.expect(expectedThrowable); - expected.expectMessage(expectedMessage); - - try { - getViolations(rule, code); - } catch (PMDException pmdE) { - throw (Exception) pmdE.getCause(); - } - - } - - } diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index 1e1040fdae..418e27aaee 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -216,11 +216,12 @@ abstract class BaseParsingHelper, T : RootNode val report = Report() ctx.report = report ctx.sourceCodeFile = File(filename) + ctx.isIgnoreExceptions = false val rules = RuleSetFactory().createSingleRuleRuleSet(rule) try { p.sourceCodeProcessor.processSourceCode(StringReader(code), RuleSets(rules), ctx) } catch (e: PMDException) { - throw AssertionError(e) + throw e.cause!! } return report } From 3c273c85ec9503ac4c7bfe87dc7917cb3114ee14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Nov 2020 13:00:31 +0100 Subject: [PATCH 181/299] Improve exception handling for XPath queries Now there is a specific exception type for XPath exceptions. We add context values to help pinpointing the error --- .../sourceforge/pmd/lang/rule/XPathRule.java | 62 +++++++-------- .../lang/rule/xpath/PmdXPathException.java | 60 ++++++++++++++ .../xpath/internal/SaxonXPathRuleQuery.java | 79 ++++++++++--------- .../internal/SaxonXPathRuleQueryTest.java | 3 +- .../rule/xpath/internal/MetricFunction.java | 26 +++--- .../rule/xpath/internal/NodeIsFunction.java | 4 +- .../xpath/internal/BaseXPathFunctionTest.java | 25 +++--- .../xpath/internal/NodeIsFunctionTest.java | 73 +++++++++++++++++ .../internal/XPathMetricFunctionTest.java | 16 ++-- 9 files changed, 242 insertions(+), 106 deletions(-) create mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/PmdXPathException.java create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java index eef4f999b6..8c6f559a8e 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/XPathRule.java @@ -13,6 +13,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ContextedRuntimeException; import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.Rule; @@ -20,6 +21,7 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.annotation.DeprecatedUntil700; import net.sourceforge.pmd.lang.ast.AstProcessingStage; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException; import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; import net.sourceforge.pmd.lang.rule.xpath.internal.DeprecatedAttrLogger; import net.sourceforge.pmd.lang.rule.xpath.internal.SaxonXPathRuleQuery; @@ -122,53 +124,51 @@ public final class XPathRule extends AbstractRule { @Override public void apply(Node target, RuleContext ctx) { - if (xPathRuleQueryNeedsInitialization()) { - initXPathRuleQuery(); + getQueryMaybeInitialize(); + + List nodesWithViolation; + try { + nodesWithViolation = xpathRuleQuery.evaluate(target); + } catch (PmdXPathException e) { + throw addExceptionContext(e); } - List nodesWithViolation = xpathRuleQuery.evaluate(target); for (Node nodeWithViolation : nodesWithViolation) { addViolation(ctx, nodeWithViolation, nodeWithViolation.getImage()); } } + private ContextedRuntimeException addExceptionContext(PmdXPathException e) { + return e.addRuleName(getName()); + } - /** - * Initializes {@link #xpathRuleQuery} iff {@link #xPathRuleQueryNeedsInitialization()} is true. To select the - * engine in which the query will be run it looks at the XPath version. - */ - private void initXPathRuleQuery() { - String xpath = getXPathExpression(); - XPathVersion version = getVersion(); + private SaxonXPathRuleQuery getQueryMaybeInitialize() throws PmdXPathException { + if (xpathRuleQuery == null) { + String xpath = getXPathExpression(); + XPathVersion version = getVersion(); - if (version == null) { - throw new IllegalStateException("Invalid XPath version, should have been caught by Rule::dysfunctionReason"); + if (version == null) { + throw new IllegalStateException("Invalid XPath version, should have been caught by Rule::dysfunctionReason"); + } + + try { + xpathRuleQuery = new SaxonXPathRuleQuery(xpath, + version, + getPropertiesByPropertyDescriptor(), + getLanguage().getDefaultVersion().getLanguageVersionHandler().getXPathHandler(), + attrLogger); + } catch (PmdXPathException e) { + throw addExceptionContext(e); + } } - - xpathRuleQuery = new SaxonXPathRuleQuery(xpath, - version, - getPropertiesByPropertyDescriptor(), - getLanguage().getDefaultVersion().getLanguageVersionHandler().getXPathHandler(), - attrLogger); + return xpathRuleQuery; } - /** - * Checks if the {@link #xpathRuleQuery} is null and therefore requires initialization. - * - * @return true if {@link #xpathRuleQuery} is null - */ - private boolean xPathRuleQueryNeedsInitialization() { - return xpathRuleQuery == null; - } - @Override protected @NonNull RuleTargetSelector buildTargetSelector() { - if (xPathRuleQueryNeedsInitialization()) { - initXPathRuleQuery(); - } - List visits = xpathRuleQuery.getRuleChainVisits(); + List visits = getQueryMaybeInitialize().getRuleChainVisits(); logXPathRuleChainUsage(!visits.isEmpty()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/PmdXPathException.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/PmdXPathException.java new file mode 100644 index 0000000000..a118039d27 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/PmdXPathException.java @@ -0,0 +1,60 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.rule.xpath; + +import org.apache.commons.lang3.exception.ContextedRuntimeException; +import org.checkerframework.checker.nullness.qual.Nullable; + +import net.sf.saxon.trans.XPathException; + +/** + * Unchecked exception wrapper for {@link XPathException}. + */ +public class PmdXPathException extends ContextedRuntimeException { + + private static final String ERROR_KIND = "Kind"; + private static final String ERROR_PHASE = "Phase"; + private static final String EXPR = "Expression"; + private static final String VERSION = "Version"; + private static final String RULE_NAME = "Rule"; + + public PmdXPathException(XPathException e, Phase phase, String expression, XPathVersion version) { + super(e); + setContextValue(ERROR_KIND, getErrorKind(e)); + setContextValue(ERROR_PHASE, phase); + setContextValue(EXPR, expression); + setContextValue(VERSION, version); + } + + public Phase getPhase() { + return (Phase) getFirstContextValue(ERROR_PHASE); + } + + public PmdXPathException addRuleName(String ruleName) { + setContextValue(RULE_NAME, ruleName); + return this; + } + + public @Nullable String getRuleName() { + return (String) getFirstContextValue(RULE_NAME); + } + + private String getErrorKind(XPathException e) { + if (e.isSyntaxError()) { + return "Syntax error"; + } else if (e.isTypeError()) { + return "Type error"; + } else if (e.isStaticError()) { + return "Static error"; + } + return "Unknown error"; + } + + public enum Phase { + INITIALIZATION, + EVALUATION + } + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/SaxonXPathRuleQuery.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/SaxonXPathRuleQuery.java index 44ecf4abdd..b60b38b585 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/SaxonXPathRuleQuery.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/internal/SaxonXPathRuleQuery.java @@ -13,9 +13,13 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.commons.lang3.exception.ContextedRuntimeException; + import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.rule.XPathRule; +import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException; +import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException.Phase; import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; import net.sourceforge.pmd.lang.rule.xpath.impl.XPathHandler; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -71,7 +75,7 @@ public class SaxonXPathRuleQuery { Map> nodeNameToXPaths = new HashMap<>(); /** - * Representation of an XPath query, created at {@link #ensureInitialized()} using {@link #xpathExpr}. + * Representation of an XPath query, created at {@link #initialize()} using {@link #xpathExpr}. */ XPathExpression xpathExpression; @@ -82,12 +86,17 @@ public class SaxonXPathRuleQuery { XPathVersion version, Map, Object> properties, XPathHandler xPathHandler, - DeprecatedAttrLogger logger) { + DeprecatedAttrLogger logger) throws PmdXPathException { this.xpathExpr = xpathExpr; this.version = version; this.properties = properties; this.xPathHandler = xPathHandler; this.attrCtx = logger; + try { + initialize(); + } catch (XPathException e) { + throw wrapException(e, Phase.INITIALIZATION); + } } @@ -97,14 +106,11 @@ public class SaxonXPathRuleQuery { public List getRuleChainVisits() { - ensureInitialized(); return rulechainQueries; } public List evaluate(final Node node) { - ensureInitialized(); - final AstTreeInfo documentNode = getDocumentNodeForRootNode(node); documentNode.setAttrCtx(attrCtx); try { @@ -123,7 +129,7 @@ public class SaxonXPathRuleQuery { if (current instanceof AstNodeOwner) { results.add(((AstNodeOwner) current).getUnderlyingNode()); } else { - throw new RuntimeException("XPath rule expression returned a non-node (" + current.getClass() + "): " + current); + throw new XPathException("XPath rule expression returned a non-node (" + current.getClass() + "): " + current); } current = iterator.next(); } @@ -133,15 +139,18 @@ public class SaxonXPathRuleQuery { sortedRes.sort(RuleChainAnalyzer.documentOrderComparator()); return sortedRes; } catch (final XPathException e) { - throw new RuntimeException(xpathExpr + " had problem: " + e.getMessage(), e); + throw wrapException(e, Phase.EVALUATION); } finally { documentNode.setAttrCtx(DeprecatedAttrLogger.noop()); } } + private ContextedRuntimeException wrapException(XPathException e, Phase phase) { + return new PmdXPathException(e, phase, xpathExpr, version); + } + // test only List getExpressionsForLocalNameOrDefault(String nodeName) { - ensureInitialized(); List expressions = nodeNameToXPaths.get(nodeName); if (expressions != null) { return expressions; @@ -151,7 +160,6 @@ public class SaxonXPathRuleQuery { // test only Expression getFallbackExpr() { - ensureInitialized(); return nodeNameToXPaths.get(SaxonXPathRuleQuery.AST_ROOT).get(0); } @@ -175,39 +183,34 @@ public class SaxonXPathRuleQuery { nodeNameToXPaths.computeIfAbsent(nodeName, n -> new ArrayList<>(2)).add(expression); } - private void ensureInitialized() { - if (xpathExpression != null) { - return; - } - try { - this.configuration = Configuration.newConfiguration(); - this.configuration.setNamePool(getNamePool()); + private void initialize() throws XPathException { - StaticContextWithProperties staticCtx = new StaticContextWithProperties(this.configuration); - staticCtx.setXPathLanguageLevel(version == XPathVersion.XPATH_3_1 ? 31 : 20); - staticCtx.declareNamespace("fn", NamespaceConstant.FN); + this.configuration = Configuration.newConfiguration(); + this.configuration.setNamePool(getNamePool()); - for (final PropertyDescriptor propertyDescriptor : properties.keySet()) { - final String name = propertyDescriptor.name(); - if (!"xpath".equals(name) && !XPathRule.VERSION_DESCRIPTOR.name().equals(name)) { - staticCtx.declareProperty(propertyDescriptor); - } + StaticContextWithProperties staticCtx = new StaticContextWithProperties(this.configuration); + staticCtx.setXPathLanguageLevel(version == XPathVersion.XPATH_3_1 ? 31 : 20); + staticCtx.declareNamespace("fn", NamespaceConstant.FN); + + for (final PropertyDescriptor propertyDescriptor : properties.keySet()) { + final String name = propertyDescriptor.name(); + if (!"xpath".equals(name) && !XPathRule.VERSION_DESCRIPTOR.name().equals(name)) { + staticCtx.declareProperty(propertyDescriptor); } - - for (ExtensionFunctionDefinition fun : xPathHandler.getRegisteredExtensionFunctions()) { - StructuredQName qname = fun.getFunctionQName(); - staticCtx.declareNamespace(qname.getPrefix(), qname.getURI()); - this.configuration.registerExtensionFunction(fun); - } - - final XPathEvaluator xpathEvaluator = new XPathEvaluator(configuration); - xpathEvaluator.setStaticContext(staticCtx); - - xpathExpression = xpathEvaluator.createExpression(xpathExpr); - analyzeXPathForRuleChain(xpathEvaluator); - } catch (final XPathException e) { - throw new RuntimeException(e); } + + for (ExtensionFunctionDefinition fun : xPathHandler.getRegisteredExtensionFunctions()) { + StructuredQName qname = fun.getFunctionQName(); + staticCtx.declareNamespace(qname.getPrefix(), qname.getURI()); + this.configuration.registerExtensionFunction(fun); + } + + final XPathEvaluator xpathEvaluator = new XPathEvaluator(configuration); + xpathEvaluator.setStaticContext(staticCtx); + + xpathExpression = xpathEvaluator.createExpression(xpathExpr); + analyzeXPathForRuleChain(xpathEvaluator); + } private void analyzeXPathForRuleChain(final XPathEvaluator xpathEvaluator) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/SaxonXPathRuleQueryTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/SaxonXPathRuleQueryTest.java index d34335a1e6..454ccc12a5 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/SaxonXPathRuleQueryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/SaxonXPathRuleQueryTest.java @@ -26,6 +26,7 @@ import net.sourceforge.pmd.lang.ast.DummyNodeWithListAndEnum; import net.sourceforge.pmd.lang.ast.DummyRoot; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.RootNode; +import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException; import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; import net.sourceforge.pmd.lang.rule.xpath.impl.AbstractXPathFunctionDef; import net.sourceforge.pmd.lang.rule.xpath.impl.XPathHandler; @@ -106,7 +107,7 @@ public class SaxonXPathRuleQueryTest { DummyNodeWithListAndEnum dummy = new DummyNodeWithListAndEnum(); - expected.expect(RuntimeException.class); + expected.expect(PmdXPathException.class); expected.expectMessage(CoreMatchers.containsString("XPath rule expression returned a non-node")); expected.expectMessage(CoreMatchers.containsString("Int64Value")); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MetricFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MetricFunction.java index 8b784b6473..9e2a1c9260 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MetricFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MetricFunction.java @@ -11,7 +11,7 @@ import org.apache.commons.lang3.EnumUtils; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; -import net.sourceforge.pmd.lang.java.ast.MethodLikeNode; +import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration; import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey; import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey; import net.sourceforge.pmd.lang.metrics.MetricKey; @@ -79,13 +79,13 @@ public final class MetricFunction extends BaseJavaXPathFunction { } - static String badOperationMetricKeyMessage() { - return "This is not the name of an operation metric"; + static String badOperationMetricKeyMessage(String constantName) { + return String.format("'%s' is not the name of an operation metric", constantName); } - static String badClassMetricKeyMessage() { - return "This is not the name of a class metric"; + static String badClassMetricKeyMessage(String constantName) { + return String.format("'%s' is not the name of a class metric", constantName); } @@ -93,13 +93,13 @@ public final class MetricFunction extends BaseJavaXPathFunction { return "Incorrect node type: the 'metric' function cannot be applied"; } - private static double getMetric(Node n, String metricKeyName) { + private static double getMetric(Node n, String metricKeyName) throws XPathException { if (n instanceof ASTAnyTypeDeclaration) { return computeMetric(getClassMetricKey(metricKeyName), (ASTAnyTypeDeclaration) n); - } else if (n instanceof MethodLikeNode) { - return computeMetric(getOperationMetricKey(metricKeyName), (MethodLikeNode) n); + } else if (n instanceof ASTMethodOrConstructorDeclaration) { + return computeMetric(getOperationMetricKey(metricKeyName), (ASTMethodOrConstructorDeclaration) n); } else { - throw new IllegalStateException(genericBadNodeMessage()); + throw new XPathException(genericBadNodeMessage()); } } @@ -108,19 +108,19 @@ public final class MetricFunction extends BaseJavaXPathFunction { } - private static JavaClassMetricKey getClassMetricKey(String s) { + private static JavaClassMetricKey getClassMetricKey(String s) throws XPathException { String constantName = s.toUpperCase(Locale.ROOT); if (!CLASS_METRIC_KEY_MAP.containsKey(constantName)) { - throw new IllegalArgumentException(badClassMetricKeyMessage()); + throw new XPathException(badClassMetricKeyMessage(constantName)); } return CLASS_METRIC_KEY_MAP.get(constantName); } - private static JavaOperationMetricKey getOperationMetricKey(String s) { + private static JavaOperationMetricKey getOperationMetricKey(String s) throws XPathException { String constantName = s.toUpperCase(Locale.ROOT); if (!OPERATION_METRIC_KEY_MAP.containsKey(constantName)) { - throw new IllegalArgumentException(badOperationMetricKeyMessage()); + throw new XPathException(badOperationMetricKeyMessage(constantName)); } return OPERATION_METRIC_KEY_MAP.get(constantName); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunction.java index 436830c00b..19100f1eec 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunction.java @@ -21,7 +21,9 @@ public final class NodeIsFunction extends BaseRewrittenFunction, JavaNo try { return Class.forName("net.sourceforge.pmd.lang.java.ast.AST" + constantArg); } catch (ClassNotFoundException e) { - throw new XPathException("No class named AST" + constantArg); + XPathException xpathE = new XPathException("No class named AST" + constantArg); + xpathE.setIsStaticError(true); + throw xpathE; } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java index dd41f4315a..e5bc84139b 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java @@ -4,6 +4,9 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + import java.util.function.Consumer; import org.checkerframework.checker.nullness.qual.NonNull; @@ -16,6 +19,7 @@ import net.sourceforge.pmd.lang.ast.test.TestUtilsKt; import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest; import net.sourceforge.pmd.lang.rule.XPathRule; +import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException; import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; /** @@ -25,6 +29,7 @@ import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; public class BaseXPathFunctionTest extends BaseNonParserTest { private static final String VIOLATION_MESSAGE = "violation"; + private static final String RULE_NAME_PLACEHOLDER = "$rule_name"; private @NonNull Report executeRule(Rule rule, String code) { @@ -33,6 +38,7 @@ public class BaseXPathFunctionTest extends BaseNonParserTest { protected Rule makeXpathRuleFromXPath(String xpath) { XPathRule rule = new XPathRule(XPathVersion.DEFAULT, xpath); + rule.setName("$rule_name"); rule.setMessage(VIOLATION_MESSAGE); rule.setLanguage(LanguageRegistry.getLanguage(JavaLanguageModule.NAME)); return rule; @@ -44,25 +50,16 @@ public class BaseXPathFunctionTest extends BaseNonParserTest { } - protected void testWithExpectedException(String xpath, - String code, - Class exceptionClass, - Consumer exceptionSpec) { + protected void testWithExpectedException(String xpath, + String code, + Consumer exceptionSpec) { Rule rule = makeXpathRuleFromXPath(xpath); - E thrown = Assert.assertThrows(exceptionClass, () -> executeRule(rule, code)); + PmdXPathException thrown = Assert.assertThrows(PmdXPathException.class, () -> executeRule(rule, code)); exceptionSpec.accept(thrown); - } - - - protected void testWithExpectedException(String xpath, - String code, - Class exceptionClass, - String expectMessage) { - - testWithExpectedException(xpath, code, exceptionClass, thrown -> Assert.assertEquals("Wrong message", expectMessage, thrown.getMessage())); + assertThat(thrown.getRuleName(), equalTo(RULE_NAME_PLACEHOLDER)); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java new file mode 100644 index 0000000000..6aee608993 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java @@ -0,0 +1,73 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.xpath.internal; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.junit.Test; + +import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException.Phase; + +/** + * @author Clément Fournier + * @since 6.0.0 + */ +public class NodeIsFunctionTest extends BaseXPathFunctionTest { + + @Test + public void testWellFormedNodeName() { + Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceDeclaration[pmd-java:nodeIs('ClassOrInterfaceDeclaration')]"); + String code = "class Foo { Foo() {} void bar() {}}"; + + assertReportSize(rule, code, 1); + } + + @Test + public void testNodeNameStaticallyUnknown() { + Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceDeclaration[pmd-java:nodeIs(name())]"); + String code = "class Foo { Foo() {} void bar() {}}"; + + assertReportSize(rule, code, 1); + } + + + @Test + public void testWellFormedNodeNameForSupertype() { + Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceDeclaration[pmd-java:nodeIs('AnyTypeDeclaration')]"); + String code = "class Foo { Foo() {} void bar() {}}"; + + assertReportSize(rule, code, 1); + } + + + + @Test + public void testNonExistentNodeName() { + testWithExpectedException("//MethodDeclaration[pmd-java:nodeIs('ohio') > 1]", + "class Moo { void foo() {if(true){}} }", + e -> { + assertThat(e.getMessage(), containsString("ASTohio")); + assertThat(e.getPhase(), equalTo(Phase.INITIALIZATION)); + }); + + } + + + @Test + public void testNonExistentNodeNameStaticallyUnknown() { + testWithExpectedException("//MethodDeclaration[pmd-java:nodeIs(name() || 'qqq')]", + "class Moo { void foo() {if(true){}} }", + e -> { + assertThat(e.getMessage(), containsString("MethodDeclarationqqq")); + assertThat(e.getPhase(), equalTo(Phase.EVALUATION)); + }); + + } + + +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/XPathMetricFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/XPathMetricFunctionTest.java index 9017c4a5e9..c6006d5d3a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/XPathMetricFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/XPathMetricFunctionTest.java @@ -4,6 +4,9 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; + import org.junit.Test; import net.sourceforge.pmd.Rule; @@ -45,8 +48,7 @@ public class XPathMetricFunctionTest extends BaseXPathFunctionTest { public void testNonexistentMetric() { testWithExpectedException("//ConstructorDeclaration[pmd-java:metric('FOOBAR') > 1]", "class Joo { Joo() {if(true){}} }", - IllegalArgumentException.class, - MetricFunction.badOperationMetricKeyMessage()); + e -> assertThat(e.getMessage(), containsString(MetricFunction.badOperationMetricKeyMessage("FOOBAR")))); } @@ -54,8 +56,7 @@ public class XPathMetricFunctionTest extends BaseXPathFunctionTest { public void testWrongNodeTypeGeneric() { testWithExpectedException("//IfStatement[pmd-java:metric('NCSS') > 1]", "class Koo { Koo() {if(true){}} }", - IllegalStateException.class, - MetricFunction.genericBadNodeMessage()); + e -> assertThat(e.getMessage(), containsString(MetricFunction.genericBadNodeMessage()))); } @@ -63,8 +64,7 @@ public class XPathMetricFunctionTest extends BaseXPathFunctionTest { public void testWrongMetricKeyForTypeDeclaration() { testWithExpectedException("//EnumDeclaration[pmd-java:metric('CYCLO') > 1]", "enum Loo { FOO; }", - IllegalArgumentException.class, - MetricFunction.badClassMetricKeyMessage()); + e -> assertThat(e.getMessage(), containsString(MetricFunction.badClassMetricKeyMessage("CYCLO")))); } @@ -72,8 +72,8 @@ public class XPathMetricFunctionTest extends BaseXPathFunctionTest { public void testWrongMetricKeyForOperationDeclaration() { testWithExpectedException("//MethodDeclaration[pmd-java:metric('WMC') > 1]", "class Moo { void foo() {if(true){}} }", - IllegalArgumentException.class, - MetricFunction.badOperationMetricKeyMessage()); + e -> assertThat(e.getMessage(), containsString(MetricFunction.badOperationMetricKeyMessage("WMC")))); + } From b0d961c7383bded0a1d3e84b0c4c291aafa81a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Nov 2020 13:48:58 +0100 Subject: [PATCH 182/299] Test other xpath functions --- docs/_data/xpath_funs.yml | 8 +-- .../internal/BaseContextNodeTestFun.java | 9 +-- .../xpath/internal/BaseRewrittenFunction.java | 4 +- .../rule/xpath/internal/GetModifiersFun.java | 1 - .../pmd/lang/java/types/TypeTestUtil.java | 15 ++++- .../xpath/internal/BaseXPathFunctionTest.java | 5 ++ .../internal/GetModifiersFunctionsTest.java | 61 ++++++++++++++++++ .../internal/HasAnnotationXPathTest.java | 43 +++++++++++++ .../internal/MatchesSignatureXPathTest.java | 62 +++++++++++++++++++ .../java/types/InvocationMatcherTest.java | 13 ++++ 10 files changed, 205 insertions(+), 16 deletions(-) create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java diff --git a/docs/_data/xpath_funs.yml b/docs/_data/xpath_funs.yml index 32b97e219f..c4f7f4c87c 100644 --- a/docs/_data/xpath_funs.yml +++ b/docs/_data/xpath_funs.yml @@ -76,7 +76,7 @@ langs: returnType: "xs:boolean" shortDescription: "Tests whether an annotation is present on the node" description: "Returns true if the node has an annotation with the given qualified name" - notes: "The context node must be an {% jdoc jast::Annotatable %}" + notes: "The context node must be an {% jdoc jast::Annotatable %}, otherwise this returns false" parameters: - name: annotationClassName type: "xs:string" @@ -92,7 +92,7 @@ langs: Returns a sequence of the effective modifiers of a node as strings. This is documented on {% jdoc jast::ASTModifierList#getEffectiveModifiers() %}. - notes: "The context node must be an {% jdoc jast::AccessNode %}" + notes: "The context node must be an {% jdoc jast::AccessNode %}, otherwise this returns an empty sequence" parameters: examples: - code: '//MethodDeclaration[pmd-java:modifiers() = "native"]' @@ -116,7 +116,7 @@ langs: Returns a sequence of the explicit modifiers of a node as strings. This is documented on {% jdoc jast::ASTModifierList#getExplicitModifiers() %}. - notes: "The context node must be an {% jdoc jast::AccessNode %}" + notes: "The context node must be an {% jdoc jast::AccessNode %}, otherwise this returns an empty sequence" parameters: examples: - code: '//MethodDeclaration[pmd-java:explicitModifiers() = "public"]' @@ -130,7 +130,7 @@ langs: the method signature called by the context node. The format of the parameter is described on that class. - notes: "The context node must be an {% jdoc jast::InvocationNode %}" + notes: "The context node must be an {% jdoc jast::InvocationNode %}, otherwise this returns false" parameters: - name: "sig" type: "xs:string" diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java index e7d41d0b17..e40914cc75 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseContextNodeTestFun.java @@ -68,14 +68,7 @@ public class BaseContextNodeTestFun extends BaseJavaXPathFun String fullTypeName = arguments[0].head().getStringValue(); - if (klass.isInstance(contextNode)) { - return BooleanValue.get(checker.test(fullTypeName, (T) contextNode)); - } else { - throw new XPathException( - getFunctionQName().getLocalPart() - + " function may only be called on an instance of " - + klass.getSimpleName()); - } + return BooleanValue.get(klass.isInstance(contextNode) && checker.test(fullTypeName, (T) contextNode)); } }; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java index 58d1a1e801..70741581a6 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java @@ -80,8 +80,8 @@ abstract class BaseRewrittenFunction extends BaseJavaXPat public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { Node node = ((AstElementNode) context.getContextItem()).getUnderlyingNode(); if (!contextNodeType.isInstance(node)) { - throw new XPathException( - "Invalid node type: " + node.getXPathNodeName() + ", expected " + contextNodeType); + // we could report that as an error + return BooleanValue.FALSE; } String arg = arguments[0].head().getStringValue(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFun.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFun.java index d9713ba452..f8d7c0fc3f 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFun.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFun.java @@ -64,7 +64,6 @@ public final class GetModifiersFun extends BaseJavaXPathFunction { public Sequence call(XPathContext context, Sequence[] arguments) { Node contextNode = ((AstElementNode) context.getContextItem()).getUnderlyingNode(); - if (contextNode instanceof AccessNode) { ASTModifierList modList = ((AccessNode) contextNode).getModifiers(); Set mods = explicit ? modList.getExplicitModifiers() diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index 4df3d730d5..62ff041540 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -26,6 +26,7 @@ import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol; import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol; import net.sourceforge.pmd.lang.java.symbols.internal.UnresolvedClassStore; import net.sourceforge.pmd.util.OptionalBool; +import net.sourceforge.pmd.util.StringUtil; /** * Public utilities to test the type of nodes. @@ -455,7 +456,15 @@ public final class TypeTestUtil { if (isChar(source, i, c)) { return i + 1; } - throw new IllegalArgumentException("Expected " + c + " at index " + i); + throw newParseException(source, i, "character '" + c + "'"); + } + + private static RuntimeException newParseException(String source, int i, String expectedWhat) { + final String indent = " "; + String message = "Expected " + expectedWhat + " at index " + i + ":\n"; + message += indent + "\"" + StringUtil.escapeJava(source) + "\"\n"; + message += indent + StringUtils.repeat(' ', i + 1) + '^' + "\n"; + return new IllegalArgumentException(message); } private static boolean isChar(String source, int i, char c) { @@ -475,6 +484,10 @@ public final class TypeTestUtil { || source.charAt(i) == '.')) { i++; } + if (i == start) { + throw newParseException(source, i, "type"); + } + AssertionUtil.assertValidJavaBinaryName(source.substring(start, i)); // array dimensions while (isChar(source, i, '[')) { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java index e5bc84139b..30d13bc83f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java @@ -50,6 +50,11 @@ public class BaseXPathFunctionTest extends BaseNonParserTest { } + protected void assertFinds(Rule rule, int numViolations, String code) { + assertReportSize(rule, code, numViolations); + } + + protected void testWithExpectedException(String xpath, String code, Consumer exceptionSpec) { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java new file mode 100644 index 0000000000..0d65365d9a --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java @@ -0,0 +1,61 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.xpath.internal; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.MatcherAssert.assertThat; + +import org.junit.Test; + +import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException.Phase; + +/** + * @author Clément Fournier + * @since 6.0.0 + */ +public class GetModifiersFunctionsTest extends BaseXPathFunctionTest { + + + @Test + public void testEffectiveModifiers() { + Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceDeclaration[pmd-java:modifiers() = ('public', 'abstract')]"); + String code = "interface O { class Foo { } }"; + + assertReportSize(rule, code, 2); + } + + @Test + public void testExplicitModifiers() { + Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceDeclaration[pmd-java:explicitModifiers() = ('public', 'abstract')]"); + String code = "interface O { class Foo { } }"; + + assertReportSize(rule, code, 0); + } + + + @Test + public void testNotAccessNodeReturnsEmptySequence() { + Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceBody[pmd-java:modifiers()]"); + String code = "interface O { class Foo { } }"; + + assertReportSize(rule, code, 0); + } + + + @Test + public void testStaticTypeError() { + testWithExpectedException("//MethodDeclaration[(., .) is pmd-java:modifiers()]", + "class Moo { void foo() {if(true){}} }", + e -> { + assertThat(e.getMessage(), containsString("Type error")); + assertThat(e.getPhase(), equalTo(Phase.INITIALIZATION)); + }); + + } + + +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java new file mode 100644 index 0000000000..021d120668 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java @@ -0,0 +1,43 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.xpath.internal; + +import org.junit.Test; + +import net.sourceforge.pmd.Rule; + +/** + * @author Clément Fournier + * @since 6.0.0 + */ +public class HasAnnotationXPathTest extends BaseXPathFunctionTest { + + + @Test + public void testHasAnnotation() { + Rule rule = makeXpathRuleFromXPath("//MethodDeclaration[pmd-java:hasAnnotation('java.lang.Override')]"); + String code = "interface O { @Override void foo(); }"; + + assertReportSize(rule, code, 1); + } + + + @Test + public void testHasAnnotationNonQual() { + Rule rule = makeXpathRuleFromXPath("//MethodDeclaration[pmd-java:hasAnnotation('Override')]"); + String code = "interface O { @Override void foo(); }"; + + assertReportSize(rule, code, 0); //does not match + } + + @Test + public void testWrongTypeReturnsFalse() { + Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceBody[pmd-java:hasAnnotation('java.lang.Override')]"); + String code = "interface O { @Override void foo(); }"; + + assertReportSize(rule, code, 0); + } + +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java new file mode 100644 index 0000000000..d6c0403eec --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java @@ -0,0 +1,62 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.xpath.internal; + +import org.junit.Test; + +import net.sourceforge.pmd.Rule; + +/** + * @author Clément Fournier + * @since 6.0.0 + */ +public class MatchesSignatureXPathTest extends BaseXPathFunctionTest { +/* + - code: '//ConstructorCall[pmd-java:matchesSig("java.util.ArrayList#new(int)")]' + outcome: "Matches constructors calls of ArrayList with a single int parameter" + + */ + + @Test + public void testMatchSig1() { + Rule rule = makeXpathRuleFromXPath("//MethodCall[pmd-java:matchesSig('_#equals(java.lang.Object)')]"); + + assertFinds(rule, 1, "class O { { this.equals(\"\"); } }"); + assertFinds(rule, 0, "class O { { this.equals(\"\", 2); } void equals(String i, int a) {}}"); + } + + + @Test + public void testMatchSigWithReceiver() { + Rule rule = makeXpathRuleFromXPath("//MethodCall[pmd-java:matchesSig('java.lang.Enum#equals(java.lang.Object)')]"); + + assertFinds(rule, 1, "enum O {; { this.equals(\"\"); } }"); + assertFinds(rule, 0, "enum O {; { \"\".equals(\"\"); } }"); + } + + @Test + public void testMatchSigUnresolved() { + Rule rule = makeXpathRuleFromXPath("//MethodCall[pmd-java:matchesSig('java.lang.String#foobar()')]"); + + assertFinds(rule, 0, "enum O {; { \"\".foobar(); } }"); + } + + @Test + public void testMatchSigNoName() { + Rule rule = makeXpathRuleFromXPath("//MethodCall[pmd-java:matchesSig('_#_(int,int)')]"); + + assertFinds(rule, 2, "enum O {; { \"\".substring(1, 2); this.foo(1, 'c');} void foo(int a, int b) {} }"); + } + + + @Test + public void testMatchSigWrongTypeReturnsFalse() { + Rule rule = makeXpathRuleFromXPath("//EnumDeclaration[pmd-java:matchesSig('_#_(int,int)')]"); + + assertFinds(rule, 0, "enum O {; { \"\".substring(1, 2); this.foo(1, 'c');} void foo(int a, int b) {} }"); + } + + +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java index 08b1dc76c5..7c088371ae 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java @@ -5,7 +5,9 @@ package net.sourceforge.pmd.lang.java.types; import static net.sourceforge.pmd.lang.java.types.TypeTestUtil.InvocationMatcher.parse; +import static org.hamcrest.Matchers.equalTo; +import org.hamcrest.MatcherAssert; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -77,6 +79,17 @@ public class InvocationMatcherTest extends BaseNonParserTest { assertNoMatch(call, "_[]#toString()"); } + @Test + public void testWhitespaceErrorMessage() { + + parse("_#_(int,int)"); // does not fail + IllegalArgumentException e = Assert.assertThrows(IllegalArgumentException.class, () -> parse("_#_(int, int)")); + MatcherAssert.assertThat(e.getMessage(), equalTo("Expected type at index 8:\n" + + " \"_#_(int, int)\"\n" + + " ^\n")); + + } + private void assertMatch(InvocationNode call, String sig) { Assert.assertTrue(sig + " should match " + call, parse(sig).matchesCall(call)); From 1818976683efba7df5cd6c175b115334afbadbdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 13 Nov 2020 14:27:50 +0100 Subject: [PATCH 183/299] Cleanup --- .../xpath/internal/BaseXPathFunctionTest.java | 8 ++--- .../internal/GetModifiersFunctionsTest.java | 19 ++++++----- .../internal/HasAnnotationXPathTest.java | 7 ++-- .../internal/MatchesSignatureXPathTest.java | 5 --- .../xpath/internal/NodeIsFunctionTest.java | 32 +++++++++-------- .../internal/XPathMetricFunctionTest.java | 34 +++++++++++-------- 6 files changed, 52 insertions(+), 53 deletions(-) diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java index 30d13bc83f..bf5e264856 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java @@ -44,14 +44,10 @@ public class BaseXPathFunctionTest extends BaseNonParserTest { return rule; } - protected void assertReportSize(Rule rule, String code, int numViolations) { - Report report = executeRule(rule, code); - TestUtilsKt.assertSize(report, numViolations); - } - protected void assertFinds(Rule rule, int numViolations, String code) { - assertReportSize(rule, code, numViolations); + Report report = executeRule(rule, code); + TestUtilsKt.assertSize(report, numViolations); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java index 0d65365d9a..ab5afb58b6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java @@ -25,7 +25,7 @@ public class GetModifiersFunctionsTest extends BaseXPathFunctionTest { Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceDeclaration[pmd-java:modifiers() = ('public', 'abstract')]"); String code = "interface O { class Foo { } }"; - assertReportSize(rule, code, 2); + assertFinds(rule, 2, code); } @Test @@ -33,7 +33,7 @@ public class GetModifiersFunctionsTest extends BaseXPathFunctionTest { Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceDeclaration[pmd-java:explicitModifiers() = ('public', 'abstract')]"); String code = "interface O { class Foo { } }"; - assertReportSize(rule, code, 0); + assertFinds(rule, 0, code); } @@ -42,18 +42,19 @@ public class GetModifiersFunctionsTest extends BaseXPathFunctionTest { Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceBody[pmd-java:modifiers()]"); String code = "interface O { class Foo { } }"; - assertReportSize(rule, code, 0); + assertFinds(rule, 0, code); } @Test public void testStaticTypeError() { - testWithExpectedException("//MethodDeclaration[(., .) is pmd-java:modifiers()]", - "class Moo { void foo() {if(true){}} }", - e -> { - assertThat(e.getMessage(), containsString("Type error")); - assertThat(e.getPhase(), equalTo(Phase.INITIALIZATION)); - }); + testWithExpectedException( + "//MethodDeclaration[(., .) is pmd-java:modifiers()]", + "class Moo { void foo() {if(true){}} }", + e -> { + assertThat(e.getMessage(), containsString("Type error")); + assertThat(e.getPhase(), equalTo(Phase.INITIALIZATION)); + }); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java index 021d120668..fed206c337 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java @@ -20,7 +20,7 @@ public class HasAnnotationXPathTest extends BaseXPathFunctionTest { Rule rule = makeXpathRuleFromXPath("//MethodDeclaration[pmd-java:hasAnnotation('java.lang.Override')]"); String code = "interface O { @Override void foo(); }"; - assertReportSize(rule, code, 1); + assertFinds(rule, 1, code); } @@ -29,7 +29,8 @@ public class HasAnnotationXPathTest extends BaseXPathFunctionTest { Rule rule = makeXpathRuleFromXPath("//MethodDeclaration[pmd-java:hasAnnotation('Override')]"); String code = "interface O { @Override void foo(); }"; - assertReportSize(rule, code, 0); //does not match + //does not match + assertFinds(rule, 0, code); } @Test @@ -37,7 +38,7 @@ public class HasAnnotationXPathTest extends BaseXPathFunctionTest { Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceBody[pmd-java:hasAnnotation('java.lang.Override')]"); String code = "interface O { @Override void foo(); }"; - assertReportSize(rule, code, 0); + assertFinds(rule, 0, code); } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java index d6c0403eec..55eba4ef8e 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java @@ -13,11 +13,6 @@ import net.sourceforge.pmd.Rule; * @since 6.0.0 */ public class MatchesSignatureXPathTest extends BaseXPathFunctionTest { -/* - - code: '//ConstructorCall[pmd-java:matchesSig("java.util.ArrayList#new(int)")]' - outcome: "Matches constructors calls of ArrayList with a single int parameter" - - */ @Test public void testMatchSig1() { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java index 6aee608993..970be9df99 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java @@ -24,7 +24,7 @@ public class NodeIsFunctionTest extends BaseXPathFunctionTest { Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceDeclaration[pmd-java:nodeIs('ClassOrInterfaceDeclaration')]"); String code = "class Foo { Foo() {} void bar() {}}"; - assertReportSize(rule, code, 1); + assertFinds(rule, 1, code); } @Test @@ -32,7 +32,7 @@ public class NodeIsFunctionTest extends BaseXPathFunctionTest { Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceDeclaration[pmd-java:nodeIs(name())]"); String code = "class Foo { Foo() {} void bar() {}}"; - assertReportSize(rule, code, 1); + assertFinds(rule, 1, code); } @@ -41,31 +41,33 @@ public class NodeIsFunctionTest extends BaseXPathFunctionTest { Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceDeclaration[pmd-java:nodeIs('AnyTypeDeclaration')]"); String code = "class Foo { Foo() {} void bar() {}}"; - assertReportSize(rule, code, 1); + assertFinds(rule, 1, code); } @Test public void testNonExistentNodeName() { - testWithExpectedException("//MethodDeclaration[pmd-java:nodeIs('ohio') > 1]", - "class Moo { void foo() {if(true){}} }", - e -> { - assertThat(e.getMessage(), containsString("ASTohio")); - assertThat(e.getPhase(), equalTo(Phase.INITIALIZATION)); - }); + testWithExpectedException( + "//MethodDeclaration[pmd-java:nodeIs('ohio') > 1]", + "class Moo { void foo() {if(true){}} }", + e -> { + assertThat(e.getMessage(), containsString("ASTohio")); + assertThat(e.getPhase(), equalTo(Phase.INITIALIZATION)); + }); } @Test public void testNonExistentNodeNameStaticallyUnknown() { - testWithExpectedException("//MethodDeclaration[pmd-java:nodeIs(name() || 'qqq')]", - "class Moo { void foo() {if(true){}} }", - e -> { - assertThat(e.getMessage(), containsString("MethodDeclarationqqq")); - assertThat(e.getPhase(), equalTo(Phase.EVALUATION)); - }); + testWithExpectedException( + "//MethodDeclaration[pmd-java:nodeIs(name() || 'qqq')]", + "class Moo { void foo() {if(true){}} }", + e -> { + assertThat(e.getMessage(), containsString("MethodDeclarationqqq")); + assertThat(e.getPhase(), equalTo(Phase.EVALUATION)); + }); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/XPathMetricFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/XPathMetricFunctionTest.java index c6006d5d3a..1bc8b347f5 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/XPathMetricFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/XPathMetricFunctionTest.java @@ -22,7 +22,7 @@ public class XPathMetricFunctionTest extends BaseXPathFunctionTest { Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceDeclaration[pmd-java:metric('NCSS') > 0]"); String code = "class Foo { Foo() {} void bar() {}}"; - assertReportSize(rule, code, 1); + assertFinds(rule, 1, code); } @@ -31,7 +31,7 @@ public class XPathMetricFunctionTest extends BaseXPathFunctionTest { Rule rule = makeXpathRuleFromXPath("//ConstructorDeclaration[pmd-java:metric('CYCLO') > 1]"); String code = "class Goo { Goo() {if(true){}} }"; - assertReportSize(rule, code, 1); + assertFinds(rule, 1, code); } @@ -40,39 +40,43 @@ public class XPathMetricFunctionTest extends BaseXPathFunctionTest { Rule rule = makeXpathRuleFromXPath("//ConstructorDeclaration[pmd-java:metric('cYclo') > 1]"); String code = "class Hoo { Hoo() {if(true){}} }"; - assertReportSize(rule, code, 1); + assertFinds(rule, 1, code); } @Test public void testNonexistentMetric() { - testWithExpectedException("//ConstructorDeclaration[pmd-java:metric('FOOBAR') > 1]", - "class Joo { Joo() {if(true){}} }", - e -> assertThat(e.getMessage(), containsString(MetricFunction.badOperationMetricKeyMessage("FOOBAR")))); + testWithExpectedException( + "//ConstructorDeclaration[pmd-java:metric('FOOBAR') > 1]", + "class Joo { Joo() {if(true){}} }", + e -> assertThat(e.getMessage(), containsString(MetricFunction.badOperationMetricKeyMessage("FOOBAR")))); } @Test public void testWrongNodeTypeGeneric() { - testWithExpectedException("//IfStatement[pmd-java:metric('NCSS') > 1]", - "class Koo { Koo() {if(true){}} }", - e -> assertThat(e.getMessage(), containsString(MetricFunction.genericBadNodeMessage()))); + testWithExpectedException( + "//IfStatement[pmd-java:metric('NCSS') > 1]", + "class Koo { Koo() {if(true){}} }", + e -> assertThat(e.getMessage(), containsString(MetricFunction.genericBadNodeMessage()))); } @Test public void testWrongMetricKeyForTypeDeclaration() { - testWithExpectedException("//EnumDeclaration[pmd-java:metric('CYCLO') > 1]", - "enum Loo { FOO; }", - e -> assertThat(e.getMessage(), containsString(MetricFunction.badClassMetricKeyMessage("CYCLO")))); + testWithExpectedException( + "//EnumDeclaration[pmd-java:metric('CYCLO') > 1]", + "enum Loo { FOO; }", + e -> assertThat(e.getMessage(), containsString(MetricFunction.badClassMetricKeyMessage("CYCLO")))); } @Test public void testWrongMetricKeyForOperationDeclaration() { - testWithExpectedException("//MethodDeclaration[pmd-java:metric('WMC') > 1]", - "class Moo { void foo() {if(true){}} }", - e -> assertThat(e.getMessage(), containsString(MetricFunction.badOperationMetricKeyMessage("WMC")))); + testWithExpectedException( + "//MethodDeclaration[pmd-java:metric('WMC') > 1]", + "class Moo { void foo() {if(true){}} }", + e -> assertThat(e.getMessage(), containsString(MetricFunction.badOperationMetricKeyMessage("WMC")))); } 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 184/299] 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 4fe3874824f3736547d808ad37db2e1bbc47d5cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 14:57:25 +0100 Subject: [PATCH 185/299] Simplify constructor of JavaTypeDefinitionSimple --- .../JavaTypeDefinitionSimple.java | 89 +++++++++++-------- 1 file changed, 50 insertions(+), 39 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java index 1f0fbbd7e5..4d5c2e2f61 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java @@ -12,6 +12,7 @@ import static net.sourceforge.pmd.lang.java.typeresolution.typedefinition.TypeDe import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -26,21 +27,44 @@ import java.util.logging.Logger; /* default */ class JavaTypeDefinitionSimple extends JavaTypeDefinition { + private final Class clazz; - private final JavaTypeDefinition[] genericArgs; + private JavaTypeDefinition[] genericArgs; // cached because calling clazz.getTypeParameters().length create a new array every time - private final int typeParameterCount; - private final boolean isGeneric; - private final boolean isRawType; + private int typeParameterCount; + private final int typeArgumentCount; private final JavaTypeDefinition enclosingClass; private static final Logger LOG = Logger.getLogger(JavaTypeDefinitionSimple.class.getName()); - private static final JavaTypeDefinition[] NO_GENERICS = {}; protected JavaTypeDefinitionSimple(Class clazz, JavaTypeDefinition... boundGenerics) { super(EXACT); this.clazz = clazz; + typeArgumentCount = boundGenerics.length; + if (boundGenerics.length > 0) { + genericArgs = Arrays.copyOf(boundGenerics, boundGenerics.length); + } // otherwise stays null + + enclosingClass = forClass(loadEnclosing(clazz)); + } + + private Class loadEnclosing(Class clazz) { + if (Modifier.isStatic(clazz.getModifiers())) { + return null; + } + Class enclosing = null; + try { + enclosing = clazz.getEnclosingClass(); + } catch (LinkageError e) { + if (LOG.isLoggable(Level.WARNING)) { + LOG.log(Level.WARNING, "Could not load enclosing class of " + clazz.getName() + ", due to: " + e); + } + } + return enclosing; + } + + private TypeVariable[] getTypeParameters(Class clazz) { final TypeVariable[] typeParameters; // the anonymous class can't have generics, but we may be binding generics from super classes if (clazz.isAnonymousClass()) { @@ -53,27 +77,7 @@ import java.util.logging.Logger; } else { typeParameters = clazz.getTypeParameters(); } - - typeParameterCount = typeParameters.length; - isGeneric = typeParameters.length != 0; - isRawType = isGeneric && boundGenerics.length == 0; - - if (isGeneric) { - // Generics will be lazily loaded if not already known - this.genericArgs = Arrays.copyOf(boundGenerics, typeParameterCount); - } else { - this.genericArgs = NO_GENERICS; - } - - Class enclosing = null; - try { - enclosing = clazz.getEnclosingClass(); - } catch (LinkageError e) { - if (LOG.isLoggable(Level.WARNING)) { - LOG.log(Level.WARNING, "Could not load enclosing class of " + clazz.getName() + ", due to: " + e); - } - } - enclosingClass = forClass(enclosing); + return typeParameters; } @Override @@ -86,9 +90,22 @@ import java.util.logging.Logger; return enclosingClass; } + @Override + public boolean isRawType() { + return isGeneric() && typeArgumentCount == 0; + } + @Override public boolean isGeneric() { - return isGeneric; + return getTypeParameterCount() != 0; + } + + @Override + public int getTypeParameterCount() { + if (typeParameterCount == -1) { + typeParameterCount = getTypeParameters(clazz).length; + } + return typeParameterCount; } private JavaTypeDefinition getGenericType(final String parameterName, Method method, @@ -131,6 +148,10 @@ import java.util.logging.Logger; @Override public JavaTypeDefinition getGenericType(final int index) { + if (genericArgs == null) { + genericArgs = new JavaTypeDefinition[getTypeParameterCount()]; + } + // Check if it has been lazily initialized first final JavaTypeDefinition cachedDefinition = genericArgs[index]; if (cachedDefinition != null) { @@ -265,11 +286,6 @@ import java.util.logging.Logger; return clazz == def.getType(); } - @Override - public int getTypeParameterCount() { - return typeParameterCount; - } - @Override public String toString() { @@ -290,7 +306,7 @@ import java.util.logging.Logger; sb.replace(sb.length() - 3, sb.length() - 1, ""); // remove last comma } - return sb.append("], isGeneric=").append(isGeneric) + return sb.append("], isGeneric=").append(isGeneric()) .append("]\n").toString(); } @@ -298,7 +314,7 @@ import java.util.logging.Logger; public String shallowString() { return new StringBuilder("JavaTypeDefinition [clazz=").append(clazz) .append(", definitionType=").append(getDefinitionType()) - .append(", isGeneric=").append(isGeneric) + .append(", isGeneric=").append(isGeneric()) .append("]\n").toString(); } @@ -411,11 +427,6 @@ import java.util.logging.Logger; return 1; } - @Override - public boolean isRawType() { - return isRawType; - } - @Override public boolean isIntersectionType() { return false; From 00d76b03ab1bfd7b6eb517be1f565464d26dcf72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 15:01:40 +0100 Subject: [PATCH 186/299] Restrict caching to classes loaded by the bootstrap loader --- .../typedefinition/JavaTypeDefinition.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java index 101fa8a9bd..b490169216 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java @@ -16,7 +16,10 @@ import org.apache.commons.lang3.ArrayUtils; public abstract class JavaTypeDefinition implements TypeDefinition { - // contains non-generic and raw EXACT types + // Contains non-generic and raw EXACT types + // Only contains classes loaded by the bootstrap classloader so as not to + // keep references to the user classloader alive + // This is enough to cache eg Object, String, and other common types private static final Map, JavaTypeDefinition> CLASS_EXACT_TYPE_DEF_CACHE = new ConcurrentHashMap<>(); private final TypeDefinitionType definitionType; @@ -76,7 +79,10 @@ public abstract class JavaTypeDefinition implements TypeDefinition { return null; // Can happen if a parent class references a class not in classpath } - CLASS_EXACT_TYPE_DEF_CACHE.put(clazz, newDef); + if (clazz.getClassLoader() == null) { + // loaded by the bootstrap classloader + CLASS_EXACT_TYPE_DEF_CACHE.put(clazz, newDef); + } return newDef; } From 307eacd20aecd7cabda70e3df27f78b5bd9d91a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 15:34:06 +0100 Subject: [PATCH 187/299] Fix errors --- .../typedefinition/JavaTypeDefinitionSimple.java | 15 ++++++++------- .../pmd/typeresolution/ClassTypeResolverTest.java | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java index 4d5c2e2f61..3c24a2cad9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java @@ -31,7 +31,7 @@ import java.util.logging.Logger; private final Class clazz; private JavaTypeDefinition[] genericArgs; // cached because calling clazz.getTypeParameters().length create a new array every time - private int typeParameterCount; + private int typeParameterCount = -1; private final int typeArgumentCount; private final JavaTypeDefinition enclosingClass; @@ -294,15 +294,12 @@ import java.util.logging.Logger; .append(", genericArgs=["); // Forcefully resolve all generic types - for (int i = 0; i < genericArgs.length; i++) { - getGenericType(i); - } - - for (final JavaTypeDefinition jtd : genericArgs) { + for (int i = 0; i < getTypeParameterCount(); i++) { + JavaTypeDefinition jtd = getGenericType(i); sb.append(jtd.shallowString()).append(", "); } - if (genericArgs.length != 0) { + if (getTypeParameterCount() != 0) { sb.replace(sb.length() - 3, sb.length() - 1, ""); // remove last comma } @@ -341,6 +338,10 @@ import java.util.logging.Logger; return false; } + if (isRawType() || otherTypeDef.isRawType()) { + return this.isRawType() == otherTypeDef.isRawType(); + } + for (int i = 0; i < getTypeParameterCount(); ++i) { // Note: we assume that cycles can only exist because of raw types if (!getGenericType(i).equals(otherTypeDef.getGenericType(i))) { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java index f2e9661ed5..f5a7add61a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/ClassTypeResolverTest.java @@ -1703,8 +1703,8 @@ public class ClassTypeResolverTest { a = forClass(JavaTypeDefinitionEquals.class); b = forClass(JavaTypeDefinitionEquals.class, forClass(List.class, a)); - assertEquals(a, b); - assertEquals(b, a); + assertNotEquals(a, b); + assertNotEquals(b, a); } @Test From 4ee7b7ae027fee6755fd4c6375fbfb37cd22149b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 16:44:27 +0100 Subject: [PATCH 188/299] Load fewer class names --- .../typeresolution/ClassTypeResolver.java | 52 ++++++++++++++++--- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java index 296504ff29..e5f237775a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java @@ -14,6 +14,7 @@ import static net.sourceforge.pmd.lang.java.typeresolution.typedefinition.TypeDe import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -21,6 +22,8 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import org.apache.commons.lang3.StringUtils; + import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.QualifiableNode; @@ -289,20 +292,24 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla /** * Set's the node's type to the found Class in the node's name (if there is a class to be found). * - * @param node - * * @return The index in the array produced by splitting the node's name by '.', which is not part of the - * class name found. Example: com.package.SomeClass.staticField.otherField, return would be 3 + * class name found. Example: com.package.SomeClass.staticField.otherField, return would be 3 */ - private int searchNodeNameForClass(TypeNode node) { + private int searchNodeNameForClass(ASTName node, String[] segments) { // this is the index from which field/method names start in the dotSplitImage array - int startIndex = node.getImage().split("\\.").length; + int startIndex = lastIndexThatMayBeAClassNameExclusive(node, segments); + if (startIndex == 0) { + return 0; + } + + String reducedImage = StringUtils.join(Arrays.asList(segments).subList(0, startIndex), '.'); // tries to find a class in the node's image by omitting the parts after each '.', example: // First try: com.package.SomeClass.staticField.otherField // Second try: com.package.SomeClass.staticField // Third try: com.package.SomeClass <- found a class! - for (String reducedImage = node.getImage();;) { + + while (StringUtils.isNotEmpty(reducedImage) && startIndex > 0) { populateType(node, reducedImage); if (node.getType() != null) { break; // we found a class! @@ -323,6 +330,35 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla return startIndex; } + private int lastIndexThatMayBeAClassNameExclusive(ASTName name, String[] segments) { + assert segments.length > 0; + + + + if (name.getParent() instanceof ASTPrimarySuffix) { + // not a class name as class name in primary + // expressions are only prefixes of the expr + return 0; + } + + JavaNode opa = name.getParent().getParent(); + if (opa.getNumChildren() > 1 + name.getParent().getIndexInParent()) { + // there is a following sibling to the primary prefix + JavaNode nextSibling = opa.getChild(name.getParent().getIndexInParent() + 1); + if (isArguments(nextSibling)) { + return segments.length - 1; + } + } + return segments.length; + } + + private boolean isArguments(JavaNode node) { + if (node instanceof ASTPrimarySuffix) { + return ((ASTPrimarySuffix) node).isArguments(); + } + return false; + } + private ASTArgumentList getArgumentList(ASTArguments args) { if (args != null) { return args.getFirstChildOfType(ASTArgumentList.class); @@ -344,7 +380,7 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla Class accessingClass = getEnclosingTypeDeclarationClass(node); String[] dotSplitImage = node.getImage().split("\\."); - int startIndex = searchNodeNameForClass(node); + int startIndex = searchNodeNameForClass(node, dotSplitImage); ASTArguments astArguments = getSuffixMethodArgs(node); ASTArgumentList astArgumentList = getArgumentList(astArguments); @@ -964,7 +1000,7 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla // restore type of the name and search again ASTName name = previousChild.getFirstChildOfType(ASTName.class); name.setTypeDefinition(null); - searchNodeNameForClass(name); + searchNodeNameForClass(name, name.getImage().split("\\.")); if (name.getTypeDefinition() != null) { // rollup from Name -> PrimaryPrefix previousChild.setTypeDefinition(name.getTypeDefinition()); From 30124fef118e215d04cd7f67f186155bd6213088 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 16:54:11 +0100 Subject: [PATCH 189/299] Don't cache enclosing class (pretty rare to hit it) --- .../typedefinition/JavaTypeDefinitionSimple.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java index 3c24a2cad9..66d3d13974 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java @@ -12,7 +12,6 @@ import static net.sourceforge.pmd.lang.java.typeresolution.typedefinition.TypeDe import java.lang.reflect.Array; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -33,7 +32,6 @@ import java.util.logging.Logger; // cached because calling clazz.getTypeParameters().length create a new array every time private int typeParameterCount = -1; private final int typeArgumentCount; - private final JavaTypeDefinition enclosingClass; private static final Logger LOG = Logger.getLogger(JavaTypeDefinitionSimple.class.getName()); @@ -45,23 +43,17 @@ import java.util.logging.Logger; if (boundGenerics.length > 0) { genericArgs = Arrays.copyOf(boundGenerics, boundGenerics.length); } // otherwise stays null - - enclosingClass = forClass(loadEnclosing(clazz)); } private Class loadEnclosing(Class clazz) { - if (Modifier.isStatic(clazz.getModifiers())) { - return null; - } - Class enclosing = null; try { - enclosing = clazz.getEnclosingClass(); + return clazz.getEnclosingClass(); } catch (LinkageError e) { if (LOG.isLoggable(Level.WARNING)) { LOG.log(Level.WARNING, "Could not load enclosing class of " + clazz.getName() + ", due to: " + e); } + return null; } - return enclosing; } private TypeVariable[] getTypeParameters(Class clazz) { @@ -87,7 +79,7 @@ import java.util.logging.Logger; @Override public JavaTypeDefinition getEnclosingClass() { - return enclosingClass; + return JavaTypeDefinition.forClass(loadEnclosing(clazz)); } @Override From 3ccdb5597a751d055b67f2501f05413e359b8b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 17:05:41 +0100 Subject: [PATCH 190/299] Make overload without varargs --- .../typedefinition/JavaTypeDefinition.java | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java index b490169216..b78853d979 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java @@ -21,6 +21,8 @@ public abstract class JavaTypeDefinition implements TypeDefinition { // keep references to the user classloader alive // This is enough to cache eg Object, String, and other common types private static final Map, JavaTypeDefinition> CLASS_EXACT_TYPE_DEF_CACHE = new ConcurrentHashMap<>(); + private static final JavaTypeDefinition[] NO_GENERICS = {}; + private final TypeDefinitionType definitionType; @@ -55,6 +57,34 @@ public abstract class JavaTypeDefinition implements TypeDefinition { } } + public static JavaTypeDefinition forClass(final Class clazz) { + if (clazz == null) { + return null; + } + + if (clazz.getClassLoader() == null) { + // loaded by the bootstrap classloader + JavaTypeDefinition typeDef = CLASS_EXACT_TYPE_DEF_CACHE.get(clazz); + + if (typeDef != null) { + return typeDef; + } + typeDef = makeWithNoCache(clazz); + CLASS_EXACT_TYPE_DEF_CACHE.put(clazz, typeDef); + return typeDef; + } + + return makeWithNoCache(clazz); + } + + private static JavaTypeDefinitionSimple makeWithNoCache(Class clazz) { + try { + return new JavaTypeDefinitionSimple(clazz, JavaTypeDefinition.NO_GENERICS); + } catch (final NoClassDefFoundError e) { + return null; // Can happen if a parent class references a class not in classpath + } + } + public static JavaTypeDefinition forClass(final Class clazz, JavaTypeDefinition... boundGenerics) { if (clazz == null) { return null; @@ -66,25 +96,7 @@ public abstract class JavaTypeDefinition implements TypeDefinition { return new JavaTypeDefinitionSimple(clazz, boundGenerics); } - final JavaTypeDefinition typeDef = CLASS_EXACT_TYPE_DEF_CACHE.get(clazz); - - if (typeDef != null) { - return typeDef; - } - - final JavaTypeDefinition newDef; - try { - newDef = new JavaTypeDefinitionSimple(clazz); - } catch (final NoClassDefFoundError e) { - return null; // Can happen if a parent class references a class not in classpath - } - - if (clazz.getClassLoader() == null) { - // loaded by the bootstrap classloader - CLASS_EXACT_TYPE_DEF_CACHE.put(clazz, newDef); - } - - return newDef; + return forClass(clazz); } @Override From 51c0df8770e4aa22f9d50212be675f022c16b011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 17:35:59 +0100 Subject: [PATCH 191/299] Remove cache entirely --- .../typedefinition/JavaTypeDefinition.java | 42 +++---------------- .../JavaTypeDefinitionLower.java | 3 +- .../JavaTypeDefinitionSimple.java | 1 + 3 files changed, 8 insertions(+), 38 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java index b78853d979..09ce29655d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java @@ -8,19 +8,13 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.List; -import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang3.ArrayUtils; public abstract class JavaTypeDefinition implements TypeDefinition { - // Contains non-generic and raw EXACT types - // Only contains classes loaded by the bootstrap classloader so as not to - // keep references to the user classloader alive - // This is enough to cache eg Object, String, and other common types - private static final Map, JavaTypeDefinition> CLASS_EXACT_TYPE_DEF_CACHE = new ConcurrentHashMap<>(); + private static final JavaTypeDefinition[] NO_GENERICS = {}; @@ -58,31 +52,9 @@ public abstract class JavaTypeDefinition implements TypeDefinition { } public static JavaTypeDefinition forClass(final Class clazz) { - if (clazz == null) { - return null; - } + return clazz == Object.class ? JavaTypeDefinitionSimple.OBJECT_DEFINITION + : forClass(clazz, NO_GENERICS); // very common - if (clazz.getClassLoader() == null) { - // loaded by the bootstrap classloader - JavaTypeDefinition typeDef = CLASS_EXACT_TYPE_DEF_CACHE.get(clazz); - - if (typeDef != null) { - return typeDef; - } - typeDef = makeWithNoCache(clazz); - CLASS_EXACT_TYPE_DEF_CACHE.put(clazz, typeDef); - return typeDef; - } - - return makeWithNoCache(clazz); - } - - private static JavaTypeDefinitionSimple makeWithNoCache(Class clazz) { - try { - return new JavaTypeDefinitionSimple(clazz, JavaTypeDefinition.NO_GENERICS); - } catch (final NoClassDefFoundError e) { - return null; // Can happen if a parent class references a class not in classpath - } } public static JavaTypeDefinition forClass(final Class clazz, JavaTypeDefinition... boundGenerics) { @@ -90,13 +62,11 @@ public abstract class JavaTypeDefinition implements TypeDefinition { return null; } - // deal with generic types - if (boundGenerics.length != 0) { - // With generics there is no cache + try { return new JavaTypeDefinitionSimple(clazz, boundGenerics); + } catch (final LinkageError e) { + return null; // Can happen if a parent class references a class not in classpath } - - return forClass(clazz); } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionLower.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionLower.java index 2012d4bb5a..16700e256c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionLower.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionLower.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.java.typeresolution.typedefinition; import static net.sourceforge.pmd.lang.java.typeresolution.typedefinition.TypeDefinitionType.LOWER_WILDCARD; /* default */ class JavaTypeDefinitionLower extends JavaTypeDefinitionUpper { - private static final JavaTypeDefinition OBJECT_DEFINITION = forClass(Object.class); protected JavaTypeDefinitionLower(JavaTypeDefinition... typeList) { super(LOWER_WILDCARD, typeList); @@ -15,6 +14,6 @@ import static net.sourceforge.pmd.lang.java.typeresolution.typedefinition.TypeDe @Override protected JavaTypeDefinition firstJavaType() { - return OBJECT_DEFINITION; + return forClass(Object.class); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java index 66d3d13974..5ced2ec288 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java @@ -27,6 +27,7 @@ import java.util.logging.Logger; /* default */ class JavaTypeDefinitionSimple extends JavaTypeDefinition { + static final JavaTypeDefinitionSimple OBJECT_DEFINITION = new JavaTypeDefinitionSimple(Object.class); private final Class clazz; private JavaTypeDefinition[] genericArgs; // cached because calling clazz.getTypeParameters().length create a new array every time From b70462b79a5595c6921dc13a4c66517964ee52bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 14 Nov 2020 18:16:20 +0100 Subject: [PATCH 192/299] Update release notes, refs #2920 --- 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 bda687c234..2f8ddf80cb 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,6 +18,8 @@ This is a {{ site.pmd.release_type }} release. * pmd-core * [#1939](https://github.com/pmd/pmd/issues/1939): \[core] XPath expressions return handling +* pmd-java + * [#2911](https://github.com/pmd/pmd/issues/2911): \[java] `ClassTypeResolver#searchNodeNameForClass` leaks memory ### API Changes From 84e6c0997ba4a120bada3c831ffdc2c75fea5b72 Mon Sep 17 00:00:00 2001 From: abhishek-kumar09 Date: Sun, 15 Nov 2020 16:52:45 +0530 Subject: [PATCH 193/299] Correct Annotation Array Initialiation indents from checkstyle #8083 --- .../pmd/renderers/RenderersTests.java | 24 +++++++++---------- .../java/cpd/testdata/discardedElements.java | 2 +- .../discardedElements_no_ignore_annots.txt | 4 ++-- .../java/cpd/testdata/specialComments.java | 4 ++-- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/RenderersTests.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/RenderersTests.java index dcb68112b9..b97d81e0e2 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/RenderersTests.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/RenderersTests.java @@ -15,18 +15,18 @@ import org.junit.runners.Suite.SuiteClasses; */ @RunWith(Suite.class) @SuiteClasses({ - CodeClimateRendererTest.class, - CSVRendererTest.class, - EmacsRendererTest.class, - HTMLRendererTest.class, - IDEAJRendererTest.class, - PapariTextRendererTest.class, - SummaryHTMLRendererTest.class, - TextPadRendererTest.class, - VBHTMLRendererTest.class, - XMLRendererTest.class, - XSLTRendererTest.class, - YAHTMLRendererTest.class + CodeClimateRendererTest.class, + CSVRendererTest.class, + EmacsRendererTest.class, + HTMLRendererTest.class, + IDEAJRendererTest.class, + PapariTextRendererTest.class, + SummaryHTMLRendererTest.class, + TextPadRendererTest.class, + VBHTMLRendererTest.class, + XMLRendererTest.class, + XSLTRendererTest.class, + YAHTMLRendererTest.class }) public class RenderersTests { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements.java index 54858f8423..93869d1726 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements.java @@ -29,7 +29,7 @@ public class Foo { // class Bar @AnnotationWithParams({@Nested(1) , @Nested(2) , @Nested - }) + }) public void foo() { } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements_no_ignore_annots.txt b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements_no_ignore_annots.txt index 705523dcca..40d68ba293 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements_no_ignore_annots.txt +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/discardedElements_no_ignore_annots.txt @@ -49,8 +49,8 @@ L31 [@] 28 28 [Nested] 29 34 L32 - [}] 14 14 - [)] 15 15 + [}] 9 9 + [)] 10 10 L33 [public] 5 10 [void] 12 15 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/specialComments.java b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/specialComments.java index 7b985c8bde..93852a818c 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/specialComments.java +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/cpd/testdata/specialComments.java @@ -4,8 +4,8 @@ package foo.bar.baz; // another irrelevant comment @ MyAnnotation ("ugh") @NamedQueries({ - @NamedQuery( - )}) + @NamedQuery( + )}) public class Foo {// CPD-ON From cb5ce5d9d1ef29ab9ea2e8f8091f1cc4c82d620b Mon Sep 17 00:00:00 2001 From: Andy Robinson Date: Mon, 16 Nov 2020 12:08:47 +0000 Subject: [PATCH 194/299] [scala] adding support for CPD-ON and CPD-OFF special comments --- .../pmd/cpd/ScalaTokenAdapter.java | 59 ++++++++++++++++ .../sourceforge/pmd/cpd/ScalaTokenizer.java | 68 +++++++++++++++---- .../pmd/cpd/ScalaTokenizerTest.java | 5 ++ .../scala/cpd/testdata/special_comments.scala | 24 +++++++ .../scala/cpd/testdata/special_comments.txt | 52 ++++++++++++++ 5 files changed, 194 insertions(+), 14 deletions(-) create mode 100644 pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java create mode 100644 pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.scala create mode 100644 pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java new file mode 100644 index 0000000000..8650c67892 --- /dev/null +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java @@ -0,0 +1,59 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.cpd; + +import net.sourceforge.pmd.lang.ast.GenericToken; + +import scala.meta.tokens.Token; + +/** + * Adapts the scala.meta.tokens.Token so that it can be used with the generic BaseTokenFilter + */ +public class ScalaTokenAdapter implements GenericToken { + + private Token token; + private GenericToken previousComment; + + ScalaTokenAdapter(Token token, GenericToken comment) { + this.token = token; + this.previousComment = comment; + } + + @Override + public GenericToken getNext() { + // not required + return null; + } + + @Override + public GenericToken getPreviousComment() { + return previousComment; + } + + @Override + public String getImage() { + return token.text(); + } + + @Override + public int getBeginLine() { + return token.pos().startLine(); + } + + @Override + public int getEndLine() { + return token.pos().endLine(); + } + + @Override + public int getBeginColumn() { + return token.pos().startColumn(); + } + + @Override + public int getEndColumn() { + return token.pos().endColumn(); + } +} diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index 4685f63bd9..44ab1ee1e7 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -9,8 +9,11 @@ import java.util.Properties; import org.apache.commons.lang3.StringUtils; +import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.TokenManager; +import net.sourceforge.pmd.lang.ast.GenericToken; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.scala.ScalaLanguageHandler; import net.sourceforge.pmd.lang.scala.ScalaLanguageModule; @@ -24,7 +27,7 @@ import scala.meta.tokenizers.TokenizeException; import scala.meta.tokens.Token; /** - * Scala Tokenizer class. Uses the Scala Meta Tokenizer. + * Scala Tokenizer class. Uses the Scala Meta Tokenizer, but adapts it for use with generic filtering */ public class ScalaTokenizer implements Tokenizer { @@ -72,19 +75,21 @@ public class ScalaTokenizer implements Tokenizer { // tokenize with a filter try { scala.meta.tokens.Tokens tokens = tokenizer.tokenize(); - ScalaTokenFilter filter = new ScalaTokenFilter(tokens.iterator()); - Token token; + // use extensions to the standard PMD TokenManager and Filter + ScalaTokenManager scalaTokenManager = new ScalaTokenManager(tokens.iterator()); + ScalaTokenFilter filter = new ScalaTokenFilter(scalaTokenManager); + + GenericToken token; while ((token = filter.getNextToken()) != null) { - if (StringUtils.isEmpty(token.text())) { + if (StringUtils.isEmpty(token.getImage())) { continue; } - Position pos = token.pos(); - TokenEntry cpdToken = new TokenEntry(token.text(), + TokenEntry cpdToken = new TokenEntry(token.getImage(), filename, - pos.startLine() + 1, - pos.startColumn() + 1, - pos.endColumn() + 1); + token.getBeginLine() + 1, + token.getBeginColumn() + 1, + token.getEndColumn() + 1); tokenEntries.add(cpdToken); } } catch (Exception e) { @@ -103,19 +108,26 @@ public class ScalaTokenizer implements Tokenizer { } /** - * Token Filter skips un-helpful tokens to only register important tokens + * Implementation of the generic Token Manager, also skips un-helpful tokens to only register important tokens * and patterns. + * + * Keeps track of comments, for special comment processing */ - private static class ScalaTokenFilter { + private class ScalaTokenManager implements TokenManager { + Iterator tokenIter; Class[] skippableTokens = new Class[] { Token.Space.class, Token.Tab.class, Token.CR.class, Token.LF.class, Token.FF.class, Token.LFLF.class, Token.EOF.class }; - ScalaTokenFilter(Iterator iterator) { + GenericToken previousComment = null; + GenericToken result = null; + + ScalaTokenManager(Iterator iterator) { this.tokenIter = iterator; } - Token getNextToken() { + @Override + public GenericToken getNextToken() { if (!tokenIter.hasNext()) { return null; } @@ -125,7 +137,13 @@ public class ScalaTokenizer implements Tokenizer { token = tokenIter.next(); } while (token != null && skipToken(token) && tokenIter.hasNext()); - return token; + result = new ScalaTokenAdapter(token, previousComment); + + if (isComment(token)) { + previousComment = result; + } + + return result; } private boolean skipToken(Token token) { @@ -137,5 +155,27 @@ public class ScalaTokenizer implements Tokenizer { } return skip; } + + private boolean isComment(Token token) { + return token != null && (token.text().startsWith("//") || token.text().startsWith("/*")); + } + + @Override + public void setFileName(String fileName) { + throw new UnsupportedOperationException("setFileName deprecated"); + } } + + private class ScalaTokenFilter extends BaseTokenFilter { + ScalaTokenFilter(TokenManager tokenManager) { + super(tokenManager); + } + + @Override + protected boolean shouldStopProcessing(ScalaTokenAdapter currentToken) { + return currentToken == null; + } + + } + } diff --git a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java index 36865795c5..d09b6c84d1 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java +++ b/pmd-scala-modules/pmd-scala-common/src/test/java/net/sourceforge/pmd/cpd/ScalaTokenizerTest.java @@ -36,6 +36,11 @@ public class ScalaTokenizerTest extends CpdTextComparisonTest { doTest("sample-LiftActor"); } + @Test + public void testSuppressionComments() { + doTest("special_comments"); + } + @Test public void tokenizeFailTest() { ex.expect(TokenMgrError.class); diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.scala b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.scala new file mode 100644 index 0000000000..655a06a4a5 --- /dev/null +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.scala @@ -0,0 +1,24 @@ +// Testing CPD suppression + +// Irrelevant comment +// CPD-OFF +// CPD-ON + +case class Foo() { // special multiline comments + + /* CPD-OFF + * + * + * */ val hi = "Hello" /* This is a comment ending in CPD-ON */ + + private def bar(i: Int) : Int = { + val CPD = 40 + val OFF = 60 + CPD-OFF // This should tokenize + } + + /* CPD-OFF */ + def bar2(s: String): String = "bar2" + /* CPD-ON */ + +} diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt new file mode 100644 index 0000000000..0a54729e1a --- /dev/null +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt @@ -0,0 +1,52 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [// Testing CPD suppression] 1 27 +L3 + [// Irrelevant comment] 1 22 +L4 + [// CPD-OFF] 1 11 +L7 + [case] 1 5 + [class] 6 11 + [Foo] 12 15 + [(] 15 16 + [)] 16 17 + [{] 18 19 + [// special multiline comments] 20 49 +L9 + [/* CPD-OFF\n *\n *\n * */] 3 7 +L14 + [private] 3 10 + [def] 11 14 + [bar] 15 18 + [(] 18 19 + [i] 19 20 + [:] 20 21 + [Int] 22 25 + [)] 25 26 + [:] 27 28 + [Int] 29 32 + [=] 33 34 + [{] 35 36 +L15 + [val] 5 8 + [CPD] 9 12 + [=] 13 14 + [40] 15 17 +L16 + [val] 5 8 + [OFF] 9 12 + [=] 13 14 + [60] 15 17 +L17 + [CPD] 5 8 + [-] 8 9 + [OFF] 9 12 + [// This should tokenize] 15 38 +L18 + [}] 3 4 +L20 + [/* CPD-OFF */] 3 16 +L24 + [}] 1 2 +EOF From 035ec0a5370f409c34d305c6b86f86f16e9823a0 Mon Sep 17 00:00:00 2001 From: Andy Robinson Date: Wed, 18 Nov 2020 13:49:32 +0000 Subject: [PATCH 195/299] [scala] fixing support for CPD-ON and CPD-OFF special comments - minor fix ups as per PR comments - comments are skipped and no longer tokenised --- .../pmd/cpd/ScalaTokenAdapter.java | 19 ++++-- .../sourceforge/pmd/cpd/ScalaTokenizer.java | 23 +++---- .../scala/cpd/testdata/sample-LiftActor.txt | 65 ------------------- .../scala/cpd/testdata/special_comments.txt | 12 ---- 4 files changed, 23 insertions(+), 96 deletions(-) diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java index 8650c67892..c4842eccf9 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java @@ -23,8 +23,7 @@ public class ScalaTokenAdapter implements GenericToken { @Override public GenericToken getNext() { - // not required - return null; + throw new UnsupportedOperationException(); } @Override @@ -39,21 +38,29 @@ public class ScalaTokenAdapter implements GenericToken { @Override public int getBeginLine() { - return token.pos().startLine(); + return token.pos().startLine() + 1; } @Override public int getEndLine() { - return token.pos().endLine(); + return token.pos().endLine() + 1; } @Override public int getBeginColumn() { - return token.pos().startColumn(); + return token.pos().startColumn() + 1; } @Override public int getEndColumn() { - return token.pos().endColumn(); + return token.pos().endColumn() + 1; + } + + @Override + public String toString() { + return "ScalaTokenAdapter{" + + "token=" + token + + ", previousComment=" + previousComment + + "}"; } } diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index 44ab1ee1e7..a7a009b0d0 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -87,9 +87,9 @@ public class ScalaTokenizer implements Tokenizer { } TokenEntry cpdToken = new TokenEntry(token.getImage(), filename, - token.getBeginLine() + 1, - token.getBeginColumn() + 1, - token.getEndColumn() + 1); + token.getBeginLine(), + token.getBeginColumn(), + token.getEndColumn()); tokenEntries.add(cpdToken); } } catch (Exception e) { @@ -108,7 +108,7 @@ public class ScalaTokenizer implements Tokenizer { } /** - * Implementation of the generic Token Manager, also skips un-helpful tokens to only register important tokens + * Implementation of the generic Token Manager, also skips un-helpful tokens and comments to only register important tokens * and patterns. * * Keeps track of comments, for special comment processing @@ -117,7 +117,7 @@ public class ScalaTokenizer implements Tokenizer { Iterator tokenIter; Class[] skippableTokens = new Class[] { Token.Space.class, Token.Tab.class, Token.CR.class, - Token.LF.class, Token.FF.class, Token.LFLF.class, Token.EOF.class }; + Token.LF.class, Token.FF.class, Token.LFLF.class, Token.EOF.class, Token.Comment.class }; GenericToken previousComment = null; GenericToken result = null; @@ -135,15 +135,12 @@ public class ScalaTokenizer implements Tokenizer { Token token; do { token = tokenIter.next(); + if (isComment(token)) { + previousComment = new ScalaTokenAdapter(token, previousComment); + } } while (token != null && skipToken(token) && tokenIter.hasNext()); - result = new ScalaTokenAdapter(token, previousComment); - - if (isComment(token)) { - previousComment = result; - } - - return result; + return new ScalaTokenAdapter(token, previousComment); } private boolean skipToken(Token token) { @@ -157,7 +154,7 @@ public class ScalaTokenizer implements Tokenizer { } private boolean isComment(Token token) { - return token != null && (token.text().startsWith("//") || token.text().startsWith("/*")); + return token instanceof Token.Comment; } @Override diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt index 0534d3c538..6c4b243eb2 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt @@ -1,6 +1,4 @@ [Image] or [Truncated image[ Bcol Ecol -L1 - [/* Example source code copied from[ 1 4 L19 [package] 1 8 [net] 9 12 @@ -40,14 +38,10 @@ L26 [Unit] 19 23 L27 [}] 1 2 -L29 - [/**\n * The definition of a schedu[ 1 4 L32 [trait] 1 6 [LAScheduler] 7 18 [{] 19 20 -L33 - [/**\n * Execute some code on ano[ 3 6 L38 [def] 3 6 [execute] 7 14 @@ -79,8 +73,6 @@ L43 [onSameThread] 7 19 [=] 20 21 [false] 22 27 -L45 - [/**\n * Set this variable to the[ 3 6 L48 [@] 3 4 [volatile] 4 12 @@ -88,7 +80,6 @@ L48 [threadPoolSize] 17 31 [=] 32 33 [16] 34 36 - [// issue 194] 37 49 L50 [@] 3 4 [volatile] 4 12 @@ -98,8 +89,6 @@ L50 [threadPoolSize] 37 51 [*] 52 53 [25] 54 56 -L52 - [/**\n * If it's Full, then creat[ 3 6 L57 [@] 3 4 [volatile] 4 12 @@ -149,7 +138,6 @@ L64 [val] 15 18 [es] 19 21 [=] 22 23 - [// Executors.newFixedThreadPool(th[ 24 71 L65 [new] 9 12 [ThreadPoolExecutor] 13 31 @@ -282,8 +270,6 @@ L91 [ILAExecute] 13 23 [=] 24 25 [_] 26 27 -L93 - [/**\n * Execute some code on ano[ 3 6 L98 [def] 3 6 [execute] 7 14 @@ -464,8 +450,6 @@ L127 [MailboxItem] 15 26 [=] 27 28 [_] 29 30 -L129 - [/*\n def find(f: MailboxItem =>[ 5 7 L134 [def] 5 8 [remove] 9 15 @@ -588,8 +572,6 @@ L157 [\]] 71 72 [)] 72 73 [{] 74 75 -L158 - [// override def find(f: MailboxIte[ 5 78 L159 [next] 5 9 [=] 10 11 @@ -659,8 +641,6 @@ L167 [)] 42 43 L168 [}] 5 6 -L170 - [/**\n * Send a message to the Ac[ 3 6 L175 [def] 3 6 [send] 7 11 @@ -675,8 +655,6 @@ L175 [this] 28 32 [!] 33 34 [msg] 35 38 -L177 - [/**\n * Send a message to the Ac[ 3 6 L182 [def] 3 6 [!] 7 8 @@ -793,8 +771,6 @@ L199 [)] 10 11 L200 [}] 3 4 -L202 - [/**\n * This method inserts the [ 3 6 L207 [protected] 3 12 [def] 13 16 @@ -936,8 +912,6 @@ L230 [}] 5 6 L231 [}] 3 4 -L233 - [/**\n * A list of LoanWrappers t[ 3 6 L236 [protected] 3 12 [def] 13 16 @@ -949,8 +923,6 @@ L236 [\]] 52 53 [=] 54 55 [Nil] 56 59 -L238 - [/**\n * You can wrap calls aroun[ 3 6 L242 [protected] 3 12 [def] 13 16 @@ -1574,8 +1546,6 @@ L349 [}] 3 4 L350 [}] 1 2 -L352 - [/**\n * A SpecializedLiftActor des[ 1 4 L362 [class] 1 6 [MockSpecializedLiftActor] 7 31 @@ -1602,8 +1572,6 @@ L363 [\]] 45 46 [=] 47 48 [Nil] 49 52 -L365 - [/**\n * Send a message to the mo[ 3 6 L369 [override] 3 11 [def] 12 15 @@ -1630,10 +1598,6 @@ L372 [}] 5 6 L373 [}] 3 4 -L375 - [// We aren't required to implement[ 3 79 -L376 - [// since the message handler never[ 3 43 L377 [override] 3 11 [def] 12 15 @@ -1653,8 +1617,6 @@ L378 [=>] 12 14 L379 [}] 3 4 -L381 - [/**\n * Test to see if this acto[ 3 6 L384 [def] 3 6 [hasReceivedMessage_?] 7 27 @@ -1672,8 +1634,6 @@ L384 [(] 72 73 [msg] 73 76 [)] 76 77 -L386 - [/**\n * Returns the list of mess[ 3 6 L389 [def] 3 6 [messages] 7 15 @@ -1684,8 +1644,6 @@ L389 [\]] 23 24 [=] 25 26 [messagesReceived] 27 43 -L391 - [/**\n * Return the number of mes[ 3 6 L394 [def] 3 6 [messageCount] 7 19 @@ -1830,8 +1788,6 @@ L417 [msg] 24 27 L418 [}] 3 4 -L420 - [/**\n * Send a message to the Act[ 3 5 L425 [def] 3 6 [sendAndGetFuture] 7 23 @@ -1849,8 +1805,6 @@ L425 [this] 51 55 [!<] 56 58 [msg] 59 62 -L427 - [/**\n * Send a message to the Act[ 3 5 L431 [def] 3 6 [!<] 7 9 @@ -1888,8 +1842,6 @@ L434 [future] 5 11 L435 [}] 3 4 -L437 - [/**\n * Send a message to the Act[ 3 5 L442 [def] 3 6 [sendAndGetReply] 7 22 @@ -1904,8 +1856,6 @@ L442 [this] 40 44 [!?] 45 47 [msg] 48 51 -L444 - [/**\n * Send a message to the Act[ 3 5 L448 [def] 3 6 [!?] 7 9 @@ -1942,8 +1892,6 @@ L451 [get] 12 15 L452 [}] 3 4 -L455 - [/**\n * Send a message to the Act[ 3 5 L461 [def] 3 6 [sendAndGetReply] 7 22 @@ -1967,8 +1915,6 @@ L461 [,] 70 71 [msg] 72 75 [)] 75 76 -L463 - [/**\n * Send a message to the Act[ 3 5 L468 [def] 3 6 [!?] 7 9 @@ -1995,8 +1941,6 @@ L469 [,] 21 22 [timeout] 23 30 [)] 30 31 -L472 - [/**\n * Send a message to the A[ 5 7 L477 [def] 3 6 [!!] 7 9 @@ -2043,8 +1987,6 @@ L480 [)] 23 24 L481 [}] 3 4 -L483 - [/**\n * Send a message to the Act[ 3 5 L487 [def] 3 6 [!!] 7 9 @@ -2193,8 +2135,6 @@ L506 [)] 18 19 L507 [}] 3 4 -L509 - [/**\n * The Actor should call thi[ 3 5 L513 [protected] 3 12 [def] 13 16 @@ -2226,8 +2166,6 @@ L517 [}] 3 4 L518 [}] 1 2 -L520 - [/**\n * A MockLiftActor for use in[ 1 4 L529 [class] 1 6 [MockLiftActor] 7 20 @@ -2503,7 +2441,6 @@ L565 [(] 24 25 [true] 25 29 [)] 29 30 - [// access private and protected me[ 31 70 L566 [m] 9 10 [.] 10 11 @@ -2826,8 +2763,6 @@ L604 [}] 5 6 L605 [}] 1 2 -L607 - [/**\n * Java versions of Actors sh[ 1 4 L612 [class] 1 6 [LiftActorJ] 7 17 diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt index 0a54729e1a..174012eb14 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/special_comments.txt @@ -1,10 +1,4 @@ [Image] or [Truncated image[ Bcol Ecol -L1 - [// Testing CPD suppression] 1 27 -L3 - [// Irrelevant comment] 1 22 -L4 - [// CPD-OFF] 1 11 L7 [case] 1 5 [class] 6 11 @@ -12,9 +6,6 @@ L7 [(] 15 16 [)] 16 17 [{] 18 19 - [// special multiline comments] 20 49 -L9 - [/* CPD-OFF\n *\n *\n * */] 3 7 L14 [private] 3 10 [def] 11 14 @@ -42,11 +33,8 @@ L17 [CPD] 5 8 [-] 8 9 [OFF] 9 12 - [// This should tokenize] 15 38 L18 [}] 3 4 -L20 - [/* CPD-OFF */] 3 16 L24 [}] 1 2 EOF From 1e6c79c19fe716ced57f41389959db490ced7037 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 19 Nov 2020 21:08:15 +0100 Subject: [PATCH 196/299] [java] CompareObjectsWithEquals / UseEqualsToCompareStrings - Fix FN Fixes false negatives in case fields are referenced with this. Type resolution on PrimaryExpression works well enough to simply check the type. --- .../CompareObjectsWithEqualsRule.java | 113 ------------------ .../resources/category/java/errorprone.xml | 20 +++- .../ClassWithFields.java | 14 +++ .../CompareObjectsWithEqualsSample.java | 13 ++ .../ClassWithStringFields.java | 12 ++ .../UseEqualsToCompareStringsSample.java | 9 ++ .../xml/CompareObjectsWithEquals.xml | 45 ++++++- .../xml/UseEqualsToCompareStrings.xml | 53 +++++++- 8 files changed, 149 insertions(+), 130 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/ClassWithFields.java create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/CompareObjectsWithEqualsSample.java create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/ClassWithStringFields.java create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/UseEqualsToCompareStringsSample.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java deleted file mode 100644 index 62f4206976..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.errorprone; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; -import net.sourceforge.pmd.lang.java.ast.ASTEqualityExpression; -import net.sourceforge.pmd.lang.java.ast.ASTInitializer; -import net.sourceforge.pmd.lang.java.ast.ASTName; -import net.sourceforge.pmd.lang.java.ast.ASTPrimaryPrefix; -import net.sourceforge.pmd.lang.java.ast.ASTPrimarySuffix; -import net.sourceforge.pmd.lang.java.ast.ASTReferenceType; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; -import net.sourceforge.pmd.lang.java.symboltable.VariableNameDeclaration; - -public class CompareObjectsWithEqualsRule extends AbstractJavaRule { - - private boolean hasName(Node n) { - return n.getNumChildren() > 0 && n.getChild(0) instanceof ASTName; - } - - /** - * Indicate whether this node is allocating a new object. - * - * @param n - * node that might be allocating a new object - * @return true if child 0 is an AllocationExpression - */ - private boolean isAllocation(Node n) { - return n.getNumChildren() > 0 && n.getChild(0) instanceof ASTAllocationExpression - && n.getParent().getNumChildren() == 1; - } - - @Override - public Object visit(ASTEqualityExpression node, Object data) { - Node c0 = node.getChild(0).getChild(0); - Node c1 = node.getChild(1).getChild(0); - - // If either side is allocating a new object, there's no way an - // equals expression is correct - if (isAllocation(c0) || isAllocation(c1)) { - addViolation(data, node); - return data; - } - - // skip if either child is not a simple name - if (!hasName(c0) || !hasName(c1)) { - return data; - } - - // skip if either is a qualified name - if (isQualifiedName(c0.getChild(0)) || isQualifiedName(c1.getChild(0))) { - return data; - } - - // skip if either is part of a qualified name - if (isPartOfQualifiedName(node.getChild(0)) || isPartOfQualifiedName(node.getChild(1))) { - return data; - } - - // skip static initializers... missing some cases here - if (!node.getParentsOfType(ASTInitializer.class).isEmpty()) { - return data; - } - - ASTName n0 = (ASTName) c0.getChild(0); - ASTName n1 = (ASTName) c1.getChild(0); - - if (n0.getNameDeclaration() instanceof VariableNameDeclaration - && n1.getNameDeclaration() instanceof VariableNameDeclaration) { - VariableNameDeclaration nd0 = (VariableNameDeclaration) n0.getNameDeclaration(); - VariableNameDeclaration nd1 = (VariableNameDeclaration) n1.getNameDeclaration(); - - // skip array dereferences... this misses some cases - // FIXME catch comparisons btwn array elements of reference types - if (nd0.isArray() || nd1.isArray()) { - return data; - } - - if (nd0.isReferenceType() && nd1.isReferenceType()) { - ASTReferenceType type0 = ((Node) nd0.getAccessNodeParent()) - .getFirstDescendantOfType(ASTReferenceType.class); - ASTReferenceType type1 = ((Node) nd1.getAccessNodeParent()) - .getFirstDescendantOfType(ASTReferenceType.class); - // skip, if it is an enum - if (type0.getType() != null && type0.getType().equals(type1.getType()) - // It may be a custom enum class or an explicit Enum class usage - && (type0.getType().isEnum() || type0.getType() == java.lang.Enum.class)) { - return data; - } - - addViolation(data, node); - } - } - - return data; - } - - /** - * Checks whether the given node contains a qualified name, consisting of - * one ASTPrimaryPrefix and one or more ASTPrimarySuffix nodes. - * - * @param node - * the node - * @return true if it is a qualified name - */ - private boolean isPartOfQualifiedName(Node node) { - return node.getChild(0) instanceof ASTPrimaryPrefix - && !node.findChildrenOfType(ASTPrimarySuffix.class).isEmpty(); - } -} diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index d0ac48edb3..9a66253506 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1097,12 +1097,23 @@ public class Bar { language="java" since="3.2" message="Use equals() to compare object references." - class="net.sourceforge.pmd.lang.java.rule.errorprone.CompareObjectsWithEqualsRule" + class="net.sourceforge.pmd.lang.rule.XPathRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_errorprone.html#compareobjectswithequals"> Use equals() to compare object references; avoid comparing them with ==. 3 + + + + + + + + diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/ClassWithFields.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/ClassWithFields.java new file mode 100644 index 0000000000..77a7cd62b2 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/ClassWithFields.java @@ -0,0 +1,14 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.compareobjectswithequals; + +public class ClassWithFields { + private Object a; + private Object b; + + boolean test1() { + return false; + } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/CompareObjectsWithEqualsSample.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/CompareObjectsWithEqualsSample.java new file mode 100644 index 0000000000..406fc7da92 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/compareobjectswithequals/CompareObjectsWithEqualsSample.java @@ -0,0 +1,13 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.compareobjectswithequals; + +public class CompareObjectsWithEqualsSample { + boolean bar(String a, String b) { + return false; + } + + void array(int[] a, String[] b) { } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/ClassWithStringFields.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/ClassWithStringFields.java new file mode 100644 index 0000000000..5b85f10384 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/ClassWithStringFields.java @@ -0,0 +1,12 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.useequalstocomparestrings; + +public class ClassWithStringFields { + private String string1 = "a"; + private String string2 = "a"; + + public void bar() { } +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/UseEqualsToCompareStringsSample.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/UseEqualsToCompareStringsSample.java new file mode 100644 index 0000000000..245842ba6e --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/errorprone/useequalstocomparestrings/UseEqualsToCompareStringsSample.java @@ -0,0 +1,9 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.errorprone.useequalstocomparestrings; + +public class UseEqualsToCompareStringsSample { + void bar(String x) {} +} diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index 987dbbbd85..b1cf841d28 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -53,10 +53,12 @@ public class Foo { - more qualified name skippage + charAt return char - that's ok 0 array element comparison - 0 + 1 + 5 @@ -109,6 +117,8 @@ public class Foo { Comparing against new object should always return false 1 + + + + False negative - class with fields + 4 + 8,9,10,11 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml index e001d193a4..528b67d746 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml @@ -7,8 +7,11 @@ failure case using == 1 + 5 failure case using != 1 + 5 using equals, OK 0 using compareTo, OK 0 using length, OK 0 + + + + false negatives with fields + 8 + 8,9,10,11,13,14,15,16 + From ff77650801001c5e33a175b5ec5182fd47c02026 Mon Sep 17 00:00:00 2001 From: Igor Moreno Date: Thu, 19 Nov 2020 22:59:17 +0100 Subject: [PATCH 197/299] Fix typo: "an accessor" not "a" --- pmd-java/src/main/resources/category/java/bestpractices.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index cf37657286..2961f28cf1 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -71,7 +71,7 @@ public class Outer { class="net.sourceforge.pmd.lang.java.rule.bestpractices.AccessorMethodGenerationRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#accessormethodgeneration"> -When accessing a private field / method from another class, the Java compiler will generate a accessor methods +When accessing a private field / method from another class, the Java compiler will generate an accessor methods with package-private visibility. This adds overhead, and to the dex method count on Android. This situation can be avoided by changing the visibility of the field / method from private to package-private. From 3020183a2918978f5282739f176a74f318694fcb Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 20 Nov 2020 09:07:35 +0100 Subject: [PATCH 198/299] [doc] Update release notes, refs #2934 --- 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 12ef7a5855..8569a0d955 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -16,6 +16,8 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues +* java-errorprone + * [#2934](https://github.com/pmd/pmd/pull/2934): \[java] CompareObjectsWithEquals / UseEqualsToCompareStrings - False negatives with fields * pmd-core * [#1939](https://github.com/pmd/pmd/issues/1939): \[core] XPath expressions return handling From a1297dfd09b353f410127ef948ab1b7bb86aec92 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 20 Nov 2020 12:20:25 +0100 Subject: [PATCH 199/299] [java] Consider comparison with null literal as valid --- .../resources/category/java/errorprone.xml | 13 ++++++---- .../xml/CompareObjectsWithEquals.xml | 24 ++++++++++++++++++- .../xml/UseEqualsToCompareStrings.xml | 24 ++++++++++++++++++- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 9a66253506..f6f84f5924 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1108,8 +1108,12 @@ Use equals() to compare object references; avoid comparing them with ==. @@ -3460,8 +3464,9 @@ Use the equals() method instead. diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index b1cf841d28..34c7d60019 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -222,7 +222,7 @@ public class CompareWithEqualsTest { - False negative - class with fields + #2934 False negative - class with fields 4 8,9,10,11 + + + + #2934 comparison with null is valid + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml index 528b67d746..cca2301e86 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml @@ -77,7 +77,7 @@ public class UseEqualsToCompareStringsSample { - false negatives with fields + #2934 false negatives with fields 8 8,9,10,11,13,14,15,16 + + + + #2934 comparison with null is valid + 0 + From 40d4f4a910a9fb3587892fe510cea4d0be6c70d0 Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Fri, 20 Nov 2020 15:30:23 +0100 Subject: [PATCH 200/299] [cs] CPD: fix issue where ignoring using directives could not be disabled via the GUI after enabling it --- pmd-cs/src/main/java/net/sourceforge/pmd/cpd/CsTokenizer.java | 4 +--- 1 file changed, 1 insertion(+), 3 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 f56ee3d1a4..10fb5aed89 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 @@ -21,9 +21,7 @@ public class CsTokenizer extends AntlrTokenizer { private boolean ignoreUsings = false; public void setProperties(Properties properties) { - if (properties.containsKey(IGNORE_USINGS)) { - ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, "false")); - } + ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, "false")); } public void setIgnoreUsings(boolean ignoreUsings) { From 2c950641d237bbb1e830767a20e4294dffefd08f Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 20 Nov 2020 15:50:30 +0100 Subject: [PATCH 201/299] [java] Avoid false positives with literals --- .../src/main/resources/category/java/errorprone.xml | 4 ++-- .../errorprone/xml/CompareObjectsWithEquals.xml | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index f6f84f5924..70e481d925 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1113,7 +1113,7 @@ Use equals() to compare object references; avoid comparing them with ==. [pmd-java:typeIs('java.lang.Object')] [not(pmd-java:typeIs('java.lang.Enum'))] ] - [not(PrimaryExpression//NullLiteral)] + [not(PrimaryExpression/PrimaryPrefix/Literal)] ]]> @@ -3466,7 +3466,7 @@ Use the equals() method instead. diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index 34c7d60019..5e3508509a 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -245,8 +245,9 @@ public class ClassWithFields { - #2934 comparison with null is valid - 0 + #2934 comparison with null is valid and with primitive literals + 1 + 18 From 74c0c020e1c0a0cbdcaf0de9d36c1797400657f8 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 20 Nov 2020 16:18:18 +0100 Subject: [PATCH 202/299] Use new dokka javadoc path --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4085faae59..7de63c440a 100644 --- a/pom.xml +++ b/pom.xml @@ -310,7 +310,7 @@ false - ${project.basedir}/../pmd-lang-test/target/dokka/pmd-lang-test + ${project.basedir}/../pmd-lang-test/target/dokkaJavadocJar ../../pmd-lang-test/${project.version} From cda99a1629b9c47d09fc3807eed829dff3d9fc60 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 20 Nov 2020 16:41:41 +0100 Subject: [PATCH 203/299] [java] CompareObjectsWithEquals - whitelist java.lang.class and this --- .../resources/category/java/errorprone.xml | 2 ++ .../xml/CompareObjectsWithEquals.xml | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 70e481d925..67d49f7c20 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1112,8 +1112,10 @@ Use equals() to compare object references; avoid comparing them with ==. [PrimaryExpression [pmd-java:typeIs('java.lang.Object')] [not(pmd-java:typeIs('java.lang.Enum'))] + [not(pmd-java:typeIs('java.lang.Class'))] ] [not(PrimaryExpression/PrimaryPrefix/Literal)] + [not(PrimaryExpression[PrimaryPrefix/@ThisModifier = true()][not(PrimarySuffix)])] ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index 5e3508509a..52dbad9807 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -271,6 +271,27 @@ public class ClassWithFields { // basically not ok, but this case is covered already by UseEqualsToCompareStrings if (x == "String") { } } +} + ]]> + + + + #2934 this and class should be ignored + 0 + From 02135513c9ed63338e7d06e83e4b01230fe4b54e Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 20 Nov 2020 20:39:45 +0100 Subject: [PATCH 204/299] [java] CompareObjectsWithEquals: Fix more false positives --- .../resources/category/java/errorprone.xml | 8 ++-- .../xml/CompareObjectsWithEquals.xml | 47 +++++++++++++------ .../xml/UseEqualsToCompareStrings.xml | 5 +- 3 files changed, 38 insertions(+), 22 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 67d49f7c20..2e02d7e2f3 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1109,12 +1109,11 @@ Use equals() to compare object references; avoid comparing them with ==. @@ -3467,8 +3466,7 @@ Use the equals() method instead. diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index 52dbad9807..3a4b2dc453 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -20,10 +20,18 @@ public class Foo { primitives are ok 0 @@ -98,31 +106,38 @@ public class Foo { array element comparison 1 - 5 + 11 Comparing against new object should always return false - 1 + 2 + 7,9 @@ -132,7 +147,11 @@ public class Foo { qualified call in allocation 0 #2934 comparison with null is valid and with primitive literals - 1 - 18 + 2 + 18,19 diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml index cca2301e86..476b7ae864 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/UseEqualsToCompareStrings.xml @@ -6,14 +6,15 @@ failure case using == - 1 - 5 + 2 + 5,6 From 86b5948f072a789d4a95b7934c0b0814021088e2 Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Thu, 19 Nov 2020 13:48:01 -0800 Subject: [PATCH 205/299] 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 e701f52d969dc6fb9799ed748043b0f3368b21ef Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 21 Nov 2020 16:11:47 +0100 Subject: [PATCH 206/299] [java] Catch additional TypeNotPresentExceptions / LinkageErrors These exceptions are indications of an incomplete auxclasspath but should not fail PMD entirely. Incomplete auxclasspath can still result in invalid violations, either false positives or false negatives. --- .../typeresolution/ClassTypeResolver.java | 20 +++++++++++-- .../typeresolution/MethodTypeResolution.java | 30 +++++++++++++------ .../JavaTypeDefinitionSimple.java | 14 ++++++--- 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java index 296504ff29..b963618257 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/ClassTypeResolver.java @@ -508,7 +508,8 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla } } catch (final NoSuchFieldException ignored) { // swallow - } catch (final LinkageError e) { + } catch (final TypeNotPresentException | LinkageError e) { + // might be thrown by getGenericType() if (LOG.isLoggable(Level.WARNING)) { String message = "Error during type resolution of field '" + fieldImage + "' in " + typeToSearch.getType() + " due to: " + e; @@ -518,8 +519,21 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter implements Nulla return null; } - // transform the type into it's supertype - typeToSearch = typeToSearch.resolveTypeDefinition(typeToSearch.getType().getGenericSuperclass()); + try { + // transform the type into it's supertype + typeToSearch = typeToSearch.resolveTypeDefinition(typeToSearch.getType().getGenericSuperclass()); + } catch (final TypeNotPresentException | LinkageError e) { + // might be thrown by getGenericSuperclass() + // Note: This try block can't be moved up, because we need to go to the super type + // in case of NoSuchFieldException and search there. Otherwise we have a endless loop. + if (LOG.isLoggable(Level.WARNING)) { + String message = "Error during type resolution of field '" + fieldImage + "' in " + + typeToSearch.getType() + " due to: " + e; + LOG.log(Level.WARNING, message); + } + // TODO : report a missing class once we start doing that... + return null; + } } return null; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodTypeResolution.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodTypeResolution.java index 5b30f6bd52..e00ed03859 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodTypeResolution.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/MethodTypeResolution.java @@ -482,22 +482,34 @@ public final class MethodTypeResolution { // search it's supertype if (!contextClass.equals(Object.class)) { - List inheritedMethods = getApplicableMethods(context.resolveTypeDefinition(contextClass.getGenericSuperclass()), + try { + List inheritedMethods = getApplicableMethods(context.resolveTypeDefinition(contextClass.getGenericSuperclass()), methodName, typeArguments, argArity, accessingClass); - // but only add the found methods of the supertype, if they have not been overridden - // TODO: verify whether this simplified overriding detection is good enough and at the correct place - for (MethodType inherited : inheritedMethods) { - if (!result.contains(inherited)) { - result.add(inherited); + // but only add the found methods of the supertype, if they have not been overridden + // TODO: verify whether this simplified overriding detection is good enough and at the correct place + for (MethodType inherited : inheritedMethods) { + if (!result.contains(inherited)) { + result.add(inherited); + } } + } catch (TypeNotPresentException | LinkageError e) { + // might be thrown by contextClass.getGenericSuperclass() + // This is an incomplete classpath, report the missing class + LOG.log(Level.FINE, "Possible incomplete auxclasspath: Error while processing methods", e); } } // search it's interfaces - for (Type interfaceType : contextClass.getGenericInterfaces()) { - result.addAll(getApplicableMethods(context.resolveTypeDefinition(interfaceType), - methodName, typeArguments, argArity, accessingClass)); + try { + for (Type interfaceType : contextClass.getGenericInterfaces()) { + result.addAll(getApplicableMethods(context.resolveTypeDefinition(interfaceType), + methodName, typeArguments, argArity, accessingClass)); + } + } catch (TypeNotPresentException | LinkageError e) { + // might be thrown by contextClass.getGenericInterface() + // This is an incomplete classpath, report the missing class + LOG.log(Level.FINE, "Possible incomplete auxclasspath: Error while processing methods", e); } return result; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java index 1f0fbbd7e5..78d99e5fc4 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java @@ -349,13 +349,19 @@ import java.util.logging.Logger; protected Set getSuperTypeSet(Set destinationSet) { destinationSet.add(this); - if (this.clazz != Object.class) { + try { + if (this.clazz != Object.class) { - resolveTypeDefinition(clazz.getGenericSuperclass()).getSuperTypeSet(destinationSet); + resolveTypeDefinition(clazz.getGenericSuperclass()).getSuperTypeSet(destinationSet); - for (Type type : clazz.getGenericInterfaces()) { - resolveTypeDefinition(type).getSuperTypeSet(destinationSet); + for (Type type : clazz.getGenericInterfaces()) { + resolveTypeDefinition(type).getSuperTypeSet(destinationSet); + } } + } catch (TypeNotPresentException | LinkageError e) { + // might be thrown by clazz.getGenericSuperclass(), clazz.getGenericInterfaces() + // This is an incomplete classpath, report the missing class + LOG.log(Level.FINE, "Possible incomplete auxclasspath: Error while processing methods", e); } return destinationSet; From af117ac9c7fd3c9175c8232660ab9cf5f7b2a301 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 09:10:25 +0100 Subject: [PATCH 207/299] Bump m-pmd-p from 3.13.0 to 3.14.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7de63c440a..6cc41bf1d9 100644 --- a/pom.xml +++ b/pom.xml @@ -94,7 +94,7 @@ 3.0.0-M5 8.30 3.1.1 - 3.13.0 + 3.14.0 1.10.8 3.2.0 4.7.2 From 08f2ce30f02cf5c82aee95fb610c0eb88a3ea680 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 09:43:50 +0100 Subject: [PATCH 208/299] [doc] Update release notes, refs #2938 --- 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 12ef7a5855..c1021dcc1e 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -16,6 +16,8 @@ This is a {{ site.pmd.release_type }} release. ### Fixed Issues +* cs + * [#2938](https://github.com/pmd/pmd/pull/2938): \[cs] CPD: ignoring using directives could not be disabled * pmd-core * [#1939](https://github.com/pmd/pmd/issues/1939): \[core] XPath expressions return handling @@ -49,5 +51,6 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr ### External Contributions * [#2925](https://github.com/pmd/pmd/pull/2925): Cleanup: Correct annotation array initializer indents from checkstyle #8083 - [Abhishek Kumar](https://github.com/Abhishek-kumar09) +* [#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) {% endtocmaker %} From 43d291ba735988f2f2450faa39f1cff08d42b294 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 10:25:05 +0100 Subject: [PATCH 209/299] [scala] ScalaTokenizer: Make inner classes static --- .../main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index a7a009b0d0..c545b26701 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -113,14 +113,13 @@ public class ScalaTokenizer implements Tokenizer { * * Keeps track of comments, for special comment processing */ - private class ScalaTokenManager implements TokenManager { + private static class ScalaTokenManager implements TokenManager { Iterator tokenIter; Class[] skippableTokens = new Class[] { Token.Space.class, Token.Tab.class, Token.CR.class, Token.LF.class, Token.FF.class, Token.LFLF.class, Token.EOF.class, Token.Comment.class }; GenericToken previousComment = null; - GenericToken result = null; ScalaTokenManager(Iterator iterator) { this.tokenIter = iterator; @@ -163,7 +162,7 @@ public class ScalaTokenizer implements Tokenizer { } } - private class ScalaTokenFilter extends BaseTokenFilter { + private static class ScalaTokenFilter extends BaseTokenFilter { ScalaTokenFilter(TokenManager tokenManager) { super(tokenManager); } From f7be9256e030b5631e8ceffdd7f310b6a51fc76f Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 10:44:50 +0100 Subject: [PATCH 210/299] [doc] Update release notes, refs #2929, fixes #2480 --- docs/pages/release_notes.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index bda687c234..97d3c7d95e 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,7 +18,8 @@ This is a {{ site.pmd.release_type }} release. * pmd-core * [#1939](https://github.com/pmd/pmd/issues/1939): \[core] XPath expressions return handling - +* scala + * [#2480](https://github.com/pmd/pmd/issues/2480): \[scala] Support CPD suppressions ### API Changes @@ -48,4 +49,6 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr ### External Contributions +* [#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) + {% endtocmaker %} From 8cf426a4c3ea34e3bf3b2cb8e09ed8d861938e5b Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 11:15:58 +0100 Subject: [PATCH 211/299] [doc] Update text renderer format example --- docs/pages/pmd/userdocs/pmd_report_formats.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/pmd/userdocs/pmd_report_formats.md b/docs/pages/pmd/userdocs/pmd_report_formats.md index 4bbfce41ec..5ca846ac69 100644 --- a/docs/pages/pmd/userdocs/pmd_report_formats.md +++ b/docs/pages/pmd/userdocs/pmd_report_formats.md @@ -169,8 +169,8 @@ and configuration errors are reported. Example: ``` -/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java:124: Logger calls should be surrounded by log level guards. -/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java:58: This for loop can be replaced by a foreach loop +/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java:124: GuardLogStatement: Logger calls should be surrounded by log level guards. +/home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/benchmark/Benchmarker.java:58: ForLoopCanBeForeach: This for loop can be replaced by a foreach loop /home/pmd/source/pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java - PMDException: Error while parsing /home/pmd/source/pmd-core/src/test/resources/net/sourceforge/pmd/cpd/files/file_with_ISO-8859-1_encoding.java CloseResource rule violation suppressed by Annotation in /home/pmd/source/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java LoosePackageCoupling - No packages or classes specified From 7d82cec763df935c487be81a177766cc62d03c08 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 11:16:12 +0100 Subject: [PATCH 212/299] [doc] Update release notes, refs #2914, fixes #1961 --- 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 bda687c234..4e9e47e532 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,6 +18,7 @@ This is a {{ site.pmd.release_type }} release. * pmd-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 ### API Changes @@ -48,4 +49,6 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr ### External Contributions +* [#2914](https://github.com/pmd/pmd/pull/2914): \[core] Include rule name in text renderer - [Gunther Schrijvers](https://github.com/GuntherSchrijvers) + {% endtocmaker %} From 1783b1daa108726d43363ac1e4899edd8730d52b Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 13:55:48 +0100 Subject: [PATCH 213/299] Update pmd-java/src/main/resources/category/java/bestpractices.xml Co-authored-by: Igor Moreno --- pmd-java/src/main/resources/category/java/bestpractices.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 2961f28cf1..59e5e56c72 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -71,7 +71,7 @@ public class Outer { class="net.sourceforge.pmd.lang.java.rule.bestpractices.AccessorMethodGenerationRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#accessormethodgeneration"> -When accessing a private field / method from another class, the Java compiler will generate an accessor methods +When accessing private fields / methods from another class, the Java compiler will generate accessor methods with package-private visibility. This adds overhead, and to the dex method count on Android. This situation can be avoided by changing the visibility of the field / method from private to package-private. From 30b8a41bbafb2eb385e5e48c59825f0cc50f5dd7 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 13:58:01 +0100 Subject: [PATCH 214/299] [doc] Update release notes, refs #2936 --- 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 12ef7a5855..529a0b6478 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -49,5 +49,6 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr ### External Contributions * [#2925](https://github.com/pmd/pmd/pull/2925): Cleanup: Correct annotation array initializer indents from checkstyle #8083 - [Abhishek Kumar](https://github.com/Abhishek-kumar09) +* [#2936](https://github.com/pmd/pmd/pull/2936): \[java] (doc) Fix typo: "an accessor" not "a" - [Igor Moreno](https://github.com/igormoreno) {% endtocmaker %} From 9aaae814c482e9261b64545670df0e81b5dd5020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 23 Nov 2020 14:39:51 +0100 Subject: [PATCH 215/299] Handle TNPE and LinkageE when loading type params --- .../typeresolution/typedefinition/JavaTypeDefinition.java | 7 +------ .../typedefinition/JavaTypeDefinitionSimple.java | 6 +++++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java index 09ce29655d..ad6af12386 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinition.java @@ -61,12 +61,7 @@ public abstract class JavaTypeDefinition implements TypeDefinition { if (clazz == null) { return null; } - - try { - return new JavaTypeDefinitionSimple(clazz, boundGenerics); - } catch (final LinkageError e) { - return null; // Can happen if a parent class references a class not in classpath - } + return new JavaTypeDefinitionSimple(clazz, boundGenerics); } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java index 5ced2ec288..37fdab51bd 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/typeresolution/typedefinition/JavaTypeDefinitionSimple.java @@ -96,7 +96,11 @@ import java.util.logging.Logger; @Override public int getTypeParameterCount() { if (typeParameterCount == -1) { - typeParameterCount = getTypeParameters(clazz).length; + try { + typeParameterCount = getTypeParameters(clazz).length; + } catch (LinkageError | TypeNotPresentException ignored) { + typeParameterCount = 0; // don't stay stuck on -1 + } } return typeParameterCount; } From 9e1fcb241671600bab08c13d61926cf3e6a2d29f Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 14:59:58 +0100 Subject: [PATCH 216/299] [ci] Fix correct branch env var for Danger/pmd-regression-tester I guess, that was missed when I merged master. --- Dangerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dangerfile b/Dangerfile index 5c506554c7..e6e9b0fafa 100644 --- a/Dangerfile +++ b/Dangerfile @@ -21,7 +21,7 @@ end def run_pmdtester Dir.chdir('..') do begin - @base_branch = ENV['TRAVIS_BRANCH'] + @base_branch = ENV['PMD_CI_BRANCH'] @logger.info "Run against PR base #{@base_branch}" runner = PmdTester::Runner.new(get_args(@base_branch)) @new_errors, @removed_errors, @new_violations, @removed_violations, @new_configerrors, @removed_configerrors = runner.run @@ -36,7 +36,7 @@ def run_pmdtester message1 = create_message # run against master branch (if the PR is not already against master) - unless ENV['TRAVIS_BRANCH'] == 'master' + unless ENV['PMD_CI_BRANCH'] == 'master' @base_branch = 'master' @logger.info "Run against #{@base_branch}" runner = PmdTester::Runner.new(get_args(@base_branch)) From f9ca35bcd55dcdd4139aee6fef11a4f3a28e086c Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Mon, 23 Nov 2020 16:19:55 +0100 Subject: [PATCH 217/299] [scala] Fix compile errors and tests --- .../sourceforge/pmd/cpd/ScalaTokenAdapter.java | 15 ++++++++++----- .../net/sourceforge/pmd/cpd/ScalaTokenizer.java | 16 +++++----------- .../lang/scala/cpd/testdata/sample-LiftActor.txt | 1 - 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java index 9f2b7ab650..b5939f77e0 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenAdapter.java @@ -11,23 +11,23 @@ import scala.meta.tokens.Token; /** * Adapts the scala.meta.tokens.Token so that it can be used with the generic BaseTokenFilter */ -public class ScalaTokenAdapter implements GenericToken { +public class ScalaTokenAdapter implements GenericToken { private Token token; - private GenericToken previousComment; + private ScalaTokenAdapter previousComment; - ScalaTokenAdapter(Token token, GenericToken comment) { + ScalaTokenAdapter(Token token, ScalaTokenAdapter comment) { this.token = token; this.previousComment = comment; } @Override - public GenericToken getNext() { + public ScalaTokenAdapter getNext() { throw new UnsupportedOperationException(); } @Override - public GenericToken getPreviousComment() { + public ScalaTokenAdapter getPreviousComment() { return previousComment; } @@ -56,6 +56,11 @@ public class ScalaTokenAdapter implements GenericToken { return token.pos().endColumn() + 2; } + @Override + public boolean isEof() { + return token instanceof Token.EOF; + } + @Override public String toString() { return "ScalaTokenAdapter{" diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java index c545b26701..e94ed84938 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/cpd/ScalaTokenizer.java @@ -13,7 +13,6 @@ import net.sourceforge.pmd.cpd.token.internal.BaseTokenFilter; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.TokenManager; -import net.sourceforge.pmd.lang.ast.GenericToken; import net.sourceforge.pmd.lang.ast.TokenMgrError; import net.sourceforge.pmd.lang.scala.ScalaLanguageHandler; import net.sourceforge.pmd.lang.scala.ScalaLanguageModule; @@ -80,7 +79,7 @@ public class ScalaTokenizer implements Tokenizer { ScalaTokenManager scalaTokenManager = new ScalaTokenManager(tokens.iterator()); ScalaTokenFilter filter = new ScalaTokenFilter(scalaTokenManager); - GenericToken token; + ScalaTokenAdapter token; while ((token = filter.getNextToken()) != null) { if (StringUtils.isEmpty(token.getImage())) { continue; @@ -113,20 +112,20 @@ public class ScalaTokenizer implements Tokenizer { * * Keeps track of comments, for special comment processing */ - private static class ScalaTokenManager implements TokenManager { + private static class ScalaTokenManager implements TokenManager { Iterator tokenIter; Class[] skippableTokens = new Class[] { Token.Space.class, Token.Tab.class, Token.CR.class, Token.LF.class, Token.FF.class, Token.LFLF.class, Token.EOF.class, Token.Comment.class }; - GenericToken previousComment = null; + ScalaTokenAdapter previousComment = null; ScalaTokenManager(Iterator iterator) { this.tokenIter = iterator; } @Override - public GenericToken getNextToken() { + public ScalaTokenAdapter getNextToken() { if (!tokenIter.hasNext()) { return null; } @@ -155,15 +154,10 @@ public class ScalaTokenizer implements Tokenizer { private boolean isComment(Token token) { return token instanceof Token.Comment; } - - @Override - public void setFileName(String fileName) { - throw new UnsupportedOperationException("setFileName deprecated"); - } } private static class ScalaTokenFilter extends BaseTokenFilter { - ScalaTokenFilter(TokenManager tokenManager) { + ScalaTokenFilter(TokenManager tokenManager) { super(tokenManager); } diff --git a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt index cea5990e4a..09f9208c75 100644 --- a/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt +++ b/pmd-scala-modules/pmd-scala-common/src/test/resources/net/sourceforge/pmd/lang/scala/cpd/testdata/sample-LiftActor.txt @@ -80,7 +80,6 @@ L48 [threadPoolSize] 17 32 [=] 32 34 [16] 34 37 - [// issue 194] 37 50 L50 [@] 3 5 [volatile] 4 13 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 218/299] 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 219/299] 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 220/299] 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 221/299] 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 222/299] 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 223/299] 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 224/299] 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 225/299] 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 226/299] 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 227/299] 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 228/299] 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 ee2005a88c92b3c87e1be0641ec56c7ad5368454 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Tue, 24 Nov 2020 16:15:06 +0100 Subject: [PATCH 229/299] [ci] Upload/download baseline for regression tester from pmd-code.org Baseline is downloaded before executing regression tester, so that regression tester just reuses it and doesn't try to download from sourceforge. We'll only upload to pmd-code.org. Sourceforge is commented out for now. --- .ci/inc/regression-tester.inc | 14 ++++++++++---- Dangerfile | 12 +++++++++++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index 1083c81e39..ccbef27b8a 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -41,14 +41,17 @@ function regression_tester_setup_ci() { 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 sourceforge!" - log_error "Please upload manually: ${targetUrl}" + 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 @@ -67,8 +70,11 @@ function regression_tester_uploadBaseline() { cd target/reports BRANCH_FILENAME="${PMD_CI_BRANCH/\//_}" zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ - ../../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}" + # 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" diff --git a/Dangerfile b/Dangerfile index f26f7a528e..81977b1ad0 100644 --- a/Dangerfile +++ b/Dangerfile @@ -6,9 +6,10 @@ require 'logger' def run_pmdtester Dir.chdir('..') do + branch_name = "#{ENV['PMD_CI_BRANCH']}" argv = ['--local-git-repo', './pmd', '--list-of-project', './pmd/.ci/files/project-list.xml', - '--base-branch', "#{ENV['PMD_CI_BRANCH']}", + '--base-branch', branch_name, '--patch-branch', 'HEAD', '--patch-config', './pmd/.ci/files/all-java.xml', '--mode', 'online', @@ -16,6 +17,7 @@ def run_pmdtester # '--debug', ] begin + download_baseline(branch_name) runner = PmdTester::Runner.new(argv) @new_errors, @removed_errors, @new_violations, @removed_violations, @new_configerrors, @removed_configerrors = runner.run upload_report @@ -26,6 +28,14 @@ def run_pmdtester end end +def download_baseline(branch_name) + branch_filename = branch_name&.tr('/', '_') + url = "https://pmd-code.org/pmd-regression-tester/#{branch_filename}" + cmd = "mkdir -p target/reports; cd target/reports; wget #{url}" + @logger.info "Downloading baseline for branch #{branch_name}: cmd=#{cmd}" + system(cmd) +end + def upload_report Dir.chdir('target/reports') do tar_filename = "pr-#{ENV['PMD_CI_PULL_REQUEST_NUMBER']}-diff-report-#{Time.now.strftime("%Y-%m-%dT%H-%M-%SZ")}.tar" From ff030d1d2d6c7dcad606720077fc50ab6e7f48d0 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Wed, 25 Nov 2020 11:26:16 +0100 Subject: [PATCH 230/299] [ci] Update regression tester * Remove workaround for pre-downloading baseline and add baseline-download-url option * Remove unnecessary travis_wait * Add error-recovery flag --- .ci/README.md | 8 +++++--- .ci/inc/regression-tester.inc | 15 ++++++++------- Dangerfile | 28 ++++++++++++---------------- Gemfile.lock | 8 ++++---- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/.ci/README.md b/.ci/README.md index 8c1afbeab3..7614027b70 100644 --- a/.ci/README.md +++ b/.ci/README.md @@ -115,7 +115,7 @@ cd pmd git init git remote add origin https://github.com/pmd/pmd git fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin +refs/heads/${MAIN_BRANCH}:refs/remotes/origin/${MAIN_BRANCH} -git checkout --progress --force -B master refs/remotes/origin/${MAIN_BRANCH} +git checkout --progress --force -B ${MAIN_BRANCH} refs/remotes/origin/${MAIN_BRANCH} .ci/check-environment.sh @@ -159,9 +159,11 @@ You'll be dropped into a bash. Use the following script, to setup and start the ``` export MAVEN_OPTS="-Dmaven.wagon.httpconnectionManager.ttlSeconds=180 -Dmaven.wagon.http.retryHandler.count=3" -export PMD_CI_BRANCH="master" # base branch +export PMD_CI_BRANCH="master" # base branch of the pull request export PMD_CI_PULL_REQUEST_NUMBER=2913 +unset PMD_CI_SECRET_PASSPHRASE + # these are used by danger export GITHUB_EVENT_PATH=/workspaces/event.json export GITHUB_REPOSITORY=pmd/pmd @@ -179,5 +181,5 @@ git checkout --progress --force refs/remotes/pull/${PMD_CI_PULL_REQUEST_NUMBER}/ .ci/check-environment.sh -.ci/build-pr.sh +.ci/build-pr-win-macos.sh ``` diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index ccbef27b8a..5f552ca1b9 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -61,12 +61,13 @@ function regression_tester_uploadBaseline() { log_info "Generating and uploading baseline for pmdtester..." cd .. bundle config --local gemfile pmd/Gemfile - pmd/.ci/travis_wait "bundle exec pmdtester - --mode single - --local-git-repo ./pmd - --patch-branch ${PMD_CI_BRANCH} - --patch-config ./pmd/.ci/files/all-java.xml - --list-of-project ./pmd/.ci/files/project-list.xml --html-flag" + bundle exec pmdtester \ + --mode single \ + --local-git-repo ./pmd \ + --patch-branch ${PMD_CI_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/\//_}" zip -q -r ${BRANCH_FILENAME}-baseline.zip ${BRANCH_FILENAME}/ @@ -107,7 +108,7 @@ function regression_tester_executeDanger() { 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 + # Fetch more commits of the PR for danger and regression tester git fetch --no-tags --depth=50 origin +$(git rev-parse HEAD^2): log_info "Running danger on branch ${PMD_CI_BRANCH}" diff --git a/Dangerfile b/Dangerfile index 81977b1ad0..07cc07a715 100644 --- a/Dangerfile +++ b/Dangerfile @@ -14,12 +14,12 @@ def run_pmdtester '--patch-config', './pmd/.ci/files/all-java.xml', '--mode', 'online', '--auto-gen-config', + '--error-recovery', + '--baseline-download-url', 'https://pmd-code.org/pmd-regression-tester/', # '--debug', ] begin - download_baseline(branch_name) - runner = PmdTester::Runner.new(argv) - @new_errors, @removed_errors, @new_violations, @removed_violations, @new_configerrors, @removed_configerrors = runner.run + @summary = PmdTester::Runner.new(argv).run upload_report rescue StandardError => e warn("Running pmdtester failed, this message is mainly used to remind the maintainers of PMD.") @@ -28,14 +28,6 @@ def run_pmdtester end end -def download_baseline(branch_name) - branch_filename = branch_name&.tr('/', '_') - url = "https://pmd-code.org/pmd-regression-tester/#{branch_filename}" - cmd = "mkdir -p target/reports; cd target/reports; wget #{url}" - @logger.info "Downloading baseline for branch #{branch_name}: cmd=#{cmd}" - system(cmd) -end - def upload_report Dir.chdir('target/reports') do tar_filename = "pr-#{ENV['PMD_CI_PULL_REQUEST_NUMBER']}-diff-report-#{Time.now.strftime("%Y-%m-%dT%H-%M-%SZ")}.tar" @@ -49,11 +41,15 @@ def upload_report if $?.success? @logger.info "Successfully uploaded #{tar_filename} to chunk.io" - # set value of sticky to true and the message is kept after new commits are submited to the PR - message("This changeset introduces #{@new_violations} new violations, #{@new_errors} new errors and " + - "#{@new_configerrors} new configuration errors,\n" + - "removes #{@removed_violations} violations, #{@removed_errors} errors and " + - "#{@removed_configerrors} configuration errors.\n" + + # set value of sticky to true and the message is kept after new commits are submitted to the PR + message("This changeset " \ + "changes #{@summary[:violations][:changed]} violations,\n" \ + "introduces #{@summary[:violations][:new]} new violations, " \ + "#{@summary[:errors][:new]} new errors and " \ + "#{@summary[:configerrors][:new]} new configuration errors,\n" \ + "removes #{@summary[:violations][:removed]} violations, "\ + "#{@summary[:errors][:removed]} errors and " \ + "#{@summary[:configerrors][:removed]} configuration errors.\n" \ "[Full report](#{report_url.chomp}/diff/index.html)", sticky: true) else @logger.error "Error while uploading #{tar_filename} to chunk.io: #{report_url}" diff --git a/Gemfile.lock b/Gemfile.lock index 5cbf3cd857..0b12bef0a3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,6 @@ GIT remote: https://github.com/pmd/pmd-regression-tester.git - revision: 6875868e8be772807498ab46411e44d163633d64 + revision: ab54e3494917c9adf7cc236dddfee58094c2ed8a specs: pmdtester (1.1.0.pre.SNAPSHOT) differ (~> 0.1) @@ -41,7 +41,7 @@ GEM multipart-post (>= 1.2, < 3) faraday-http-cache (1.3.1) faraday (~> 0.8) - fugit (1.4.0) + fugit (1.4.1) et-orbi (~> 1.1, >= 1.1.8) raabro (~> 1.4) git (1.7.0) @@ -61,7 +61,7 @@ GEM public_suffix (4.0.6) raabro (1.4.0) rchardet (1.8.0) - rouge (3.24.0) + rouge (3.25.0) rufus-scheduler (3.6.0) fugit (~> 1.1, >= 1.1.6) safe_yaml (1.0.5) @@ -71,7 +71,7 @@ GEM slop (4.8.2) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) - tzinfo (2.0.2) + tzinfo (2.0.3) concurrent-ruby (~> 1.0) unicode-display_width (1.7.0) From f787e3a8e5d141180d1d49a50c18764ddd6fc68f Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Wed, 25 Nov 2020 11:37:30 +0100 Subject: [PATCH 231/299] [ci] Fetch more commits from master for regression testing PRs That only needed for pmd/7.0.x PRs --- .ci/inc/regression-tester.inc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.ci/inc/regression-tester.inc b/.ci/inc/regression-tester.inc index 5f552ca1b9..ffa9d25ee0 100644 --- a/.ci/inc/regression-tester.inc +++ b/.ci/inc/regression-tester.inc @@ -110,6 +110,11 @@ function regression_tester_executeDanger() { 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 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 232/299] 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 aaef0fde4e0fdd75212b8b93db18a91f32f2862d Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Wed, 25 Nov 2020 12:35:02 +0100 Subject: [PATCH 233/299] Add option to ignore sequences of literals In some cases, code may include sequences of literals that represent lists or tables of constants, such as lookup tables. Large sequences of these (particularly parts with many zeroes) will be identified by CPD as duplicates, but in practice, these are not the types of duplicates that are considered interesting. This introduces a new option for CPD (--ignore-literal-sequences) that ignores these sequences of literals, in a very similar way to how using directives for C# can already be skipped as well. For now, this functionality is restricted to C#, but it could be added for other languages as well. --- .../sourceforge/pmd/cpd/CPDConfiguration.java | 16 + .../java/net/sourceforge/pmd/cpd/GUI.java | 19 + .../net/sourceforge/pmd/cpd/Tokenizer.java | 4 + .../net/sourceforge/pmd/cpd/CsTokenizer.java | 51 ++- .../sourceforge/pmd/cpd/CsTokenizerTest.java | 21 +- .../pmd/lang/cs/cpd/testdata/listOfNumbers.cs | 8 + .../lang/cs/cpd/testdata/listOfNumbers.txt | 344 ++++++++++++++++++ .../cs/cpd/testdata/listOfNumbers_ignored.txt | 41 +++ 8 files changed, 498 insertions(+), 6 deletions(-) create mode 100644 pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs create mode 100644 pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt create mode 100644 pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index da009e56cc..262b0e5096 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -91,6 +91,9 @@ public class CPDConfiguration extends AbstractConfiguration { @Parameter(names = "--ignore-usings", description = "Ignore using directives in C#", required = false) private boolean ignoreUsings; + @Parameter(names = "--ignore-literal-sequences", description = "Ignore sequences of literals", required = false) + private boolean ignoreLiteralSequences = false; + @Parameter(names = "--skip-lexical-errors", description = "Skip files which can't be tokenized due to invalid characters instead of aborting CPD", required = false) @@ -273,6 +276,11 @@ public class CPDConfiguration extends AbstractConfiguration { } else { properties.remove(Tokenizer.IGNORE_USINGS); } + if (configuration.isIgnoreLiteralSequences()) { + properties.setProperty(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES, "true"); + } else { + properties.remove(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES); + } properties.setProperty(Tokenizer.OPTION_SKIP_BLOCKS, Boolean.toString(!configuration.isNoSkipBlocks())); properties.setProperty(Tokenizer.OPTION_SKIP_BLOCKS_PATTERN, configuration.getSkipBlocksPattern()); configuration.getLanguage().setProperties(properties); @@ -411,6 +419,14 @@ public class CPDConfiguration extends AbstractConfiguration { this.ignoreUsings = ignoreUsings; } + public boolean isIgnoreLiteralSequences() { + return ignoreLiteralSequences; + } + + public void setIgnoreLiteralSequences(boolean ignoreLiteralSequences) { + this.ignoreLiteralSequences = ignoreLiteralSequences; + } + public boolean isSkipLexicalErrors() { return skipLexicalErrors; } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java index fd7e436af2..3f44d76b34 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/GUI.java @@ -115,6 +115,10 @@ public class GUI implements CPDListener { return false; } + public boolean canIgnoreLiteralSequences() { + return false; + } + public abstract String[] extensions(); } @@ -160,6 +164,11 @@ public class GUI implements CPDListener { public boolean canIgnoreUsings() { return "cs".equals(terseName); } + + @Override + public boolean canIgnoreLiteralSequences() { + return "cs".equals(terseName); + } }; } LANGUAGE_SETS[index][0] = "by extension..."; @@ -333,6 +342,7 @@ public class GUI implements CPDListener { private JCheckBox ignoreLiteralsCheckbox = new JCheckBox("", false); private JCheckBox ignoreAnnotationsCheckbox = new JCheckBox("", false); private JCheckBox ignoreUsingsCheckbox = new JCheckBox("", false); + private JCheckBox ignoreLiteralSequencesCheckbox = new JCheckBox("", false); private JComboBox languageBox = new JComboBox<>(); private JTextField extensionField = new JTextField(); private JLabel extensionLabel = new JLabel("Extension:", SwingConstants.RIGHT); @@ -420,6 +430,7 @@ public class GUI implements CPDListener { ignoreLiteralsCheckbox.setEnabled(current.canIgnoreLiterals()); ignoreAnnotationsCheckbox.setEnabled(current.canIgnoreAnnotations()); ignoreUsingsCheckbox.setEnabled(current.canIgnoreUsings()); + ignoreLiteralSequencesCheckbox.setEnabled(current.canIgnoreLiteralSequences()); extensionField.setText(current.extensions()[0]); boolean enableExtension = current.extensions()[0].isEmpty(); extensionField.setEnabled(enableExtension); @@ -478,6 +489,13 @@ public class GUI implements CPDListener { helper.nextRow(); helper.addLabel("Ignore usings?"); helper.add(ignoreUsingsCheckbox); + helper.addLabel(""); + helper.addLabel(""); + helper.nextRow(); + + helper.nextRow(); + helper.addLabel("Ignore literal sequences?"); + helper.add(ignoreLiteralSequencesCheckbox); helper.add(goButton); helper.add(cxButton); helper.nextRow(); @@ -663,6 +681,7 @@ public class GUI implements CPDListener { config.setIgnoreLiterals(ignoreLiteralsCheckbox.isSelected()); config.setIgnoreAnnotations(ignoreAnnotationsCheckbox.isSelected()); config.setIgnoreUsings(ignoreUsingsCheckbox.isSelected()); + config.setIgnoreLiteralSequences(ignoreLiteralSequencesCheckbox.isSelected()); p.setProperty(LanguageFactory.EXTENSION, extensionField.getText()); LanguageConfig conf = languageConfigFor((String) languageBox.getSelectedItem()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java index 77e2de54d9..e6876fb960 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/Tokenizer.java @@ -11,6 +11,10 @@ public interface Tokenizer { String IGNORE_IDENTIFIERS = "ignore_identifiers"; String IGNORE_ANNOTATIONS = "ignore_annotations"; + /** + * Ignore sequences of literals (e.g, 0,0,0,0...). + */ + String OPTION_IGNORE_LITERAL_SEQUENCES = "net.sourceforge.pmd.cpd.Tokenizer.skipLiteralSequences"; /** * Ignore using directives in C#. The default value is false. */ 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 10fb5aed89..5ae96cbfa1 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 @@ -19,15 +19,21 @@ import net.sourceforge.pmd.lang.cs.antlr4.CSharpLexer; public class CsTokenizer extends AntlrTokenizer { private boolean ignoreUsings = false; + private boolean ignoreLiteralSequences = false; public void setProperties(Properties properties) { ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, "false")); + ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES, "false")); } public void setIgnoreUsings(boolean ignoreUsings) { this.ignoreUsings = ignoreUsings; } + public void setIgnoreLiteralSequences(boolean ignoreLiteralSequences) { + this.ignoreLiteralSequences = ignoreLiteralSequences; + } + @Override protected AntlrTokenManager getLexerForSource(final SourceCode sourceCode) { final CharStream charStream = AntlrTokenizer.getCharStreamFromSourceCode(sourceCode); @@ -36,7 +42,7 @@ public class CsTokenizer extends AntlrTokenizer { @Override protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) { - return new CsTokenFilter(tokenManager, ignoreUsings); + return new CsTokenFilter(tokenManager, ignoreUsings, ignoreLiteralSequences); } /** @@ -54,13 +60,16 @@ public class CsTokenizer extends AntlrTokenizer { } private final boolean ignoreUsings; + private final boolean ignoreLiteralSequences; private boolean discardingUsings = false; private boolean discardingNL = false; + private boolean discardingLiterals = false; private boolean discardCurrent = false; - CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings) { + CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences) { super(tokenManager); this.ignoreUsings = ignoreUsings; + this.ignoreLiteralSequences = ignoreLiteralSequences; } @Override @@ -72,6 +81,7 @@ public class CsTokenizer extends AntlrTokenizer { protected void analyzeTokens(final AntlrToken currentToken, final Iterable remainingTokens) { discardCurrent = false; skipUsingDirectives(currentToken, remainingTokens); + skipLiteralSequences(currentToken, remainingTokens); } private void skipUsingDirectives(final AntlrToken currentToken, final Iterable remainingTokens) { @@ -148,9 +158,44 @@ public class CsTokenizer extends AntlrTokenizer { discardingNL = currentToken.getKind() == CSharpLexer.NL; } + 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; + } + } + } + + private boolean isSequenceOfLiterals(final Iterable remainingTokens) { + boolean seenLiteral = false; + for (final AntlrToken token : remainingTokens) { + switch (token.getKind()) { + case CSharpLexer.CHARACTER_LITERAL: + case CSharpLexer.HEX_INTEGER_LITERAL: + case CSharpLexer.INTEGER_LITERAL: + case CSharpLexer.REAL_LITERAL: + seenLiteral = true; + break; // can be skipped; continue to the next token + case CSharpLexer.COMMA: + break; // can be skipped; continue to the next token + case CSharpLexer.CLOSE_BRACE: + // end of the list; skip all contents + return seenLiteral; + default: + // some other token than the expected ones; this is not a sequence of literals + return false; + } + } + return false; + } + @Override protected boolean isLanguageSpecificDiscarding() { - return discardingUsings || discardingNL || discardCurrent; + return discardingUsings || discardingNL || discardingLiterals || discardCurrent; } } } 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 6bc536a4c9..6b61c658bb 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 @@ -90,13 +90,28 @@ public class CsTokenizerTest extends CpdTextComparisonTest { doTest("tabWidth"); } - private Properties ignoreUsings() { - return properties(true); + @Test + public void testLongListsOfNumbersAreNotIgnored() { + doTest("listOfNumbers"); } - private Properties properties(boolean ignoreUsings) { + @Test + public void testLongListsOfNumbersAreIgnored() { + doTest("listOfNumbers", "_ignored", skipLiteralSequences()); + } + + private Properties ignoreUsings() { + return properties(true, false); + } + + private Properties skipLiteralSequences() { + return properties(false, true); + } + + private Properties properties(boolean ignoreUsings, boolean ignoreLiteralSequences) { Properties properties = new Properties(); properties.setProperty(Tokenizer.IGNORE_USINGS, Boolean.toString(ignoreUsings)); + properties.setProperty(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES, Boolean.toString(ignoreLiteralSequences)); return properties; } } 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 new file mode 100644 index 0000000000..a3ceee1dca --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.cs @@ -0,0 +1,8 @@ +using System; +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, + }; +} 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 new file mode 100644 index 0000000000..17d20f914d --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers.txt @@ -0,0 +1,344 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [using] 1 5 + [System] 7 12 + [;] 13 13 +L2 + [using] 1 5 + [System] 7 12 + [.] 13 13 + [Collections] 14 24 + [;] 25 25 +L3 + [using] 1 5 + [System] 7 12 + [.] 13 13 + [Collections] 14 24 + [.] 25 25 + [Generic] 26 32 + [;] 33 33 +L4 + [public] 1 6 + [class] 8 12 + [LongLists] 14 22 + [{] 24 24 +L5 + [List] 5 8 + [<] 9 9 + [byte] 10 13 + [>] 14 14 + [l] 16 16 + [=] 18 18 + [new] 20 22 + [List] 24 27 + [<] 28 28 + [byte] 29 32 + [>] 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 +L7 + [}] 5 5 + [;] 6 6 +L8 + [}] 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 new file mode 100644 index 0000000000..03f835fdca --- /dev/null +++ b/pmd-cs/src/test/resources/net/sourceforge/pmd/lang/cs/cpd/testdata/listOfNumbers_ignored.txt @@ -0,0 +1,41 @@ + [Image] or [Truncated image[ Bcol Ecol +L1 + [using] 1 5 + [System] 7 12 + [;] 13 13 +L2 + [using] 1 5 + [System] 7 12 + [.] 13 13 + [Collections] 14 24 + [;] 25 25 +L3 + [using] 1 5 + [System] 7 12 + [.] 13 13 + [Collections] 14 24 + [.] 25 25 + [Generic] 26 32 + [;] 33 33 +L4 + [public] 1 6 + [class] 8 12 + [LongLists] 14 22 + [{] 24 24 +L5 + [List] 5 8 + [<] 9 9 + [byte] 10 13 + [>] 14 14 + [l] 16 16 + [=] 18 18 + [new] 20 22 + [List] 24 27 + [<] 28 28 + [byte] 29 32 + [>] 33 33 +L7 + [;] 6 6 +L8 + [}] 1 1 +EOF From db3c636081cba5749eff56552cb71e1f60db5808 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Wed, 25 Nov 2020 14:14:49 +0100 Subject: [PATCH 234/299] [ci] Avoid mixing caches --- .github/workflows/pull-requests.yml | 4 ++-- .github/workflows/pushes.yml | 12 ++++++------ .github/workflows/troubleshooting.yml | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml index a07af8d1e5..169f000a80 100644 --- a/.github/workflows/pull-requests.yml +++ b/.github/workflows/pull-requests.yml @@ -21,9 +21,9 @@ jobs: ~/.m2/repository ~/.cache vendor/bundle - key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + key: pr-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | - ${{ runner.os }}- + pr-${{ runner.os }}- - name: Set up Ruby 2.7 uses: actions/setup-ruby@v1 with: diff --git a/.github/workflows/pushes.yml b/.github/workflows/pushes.yml index a47ef7d51a..d713ae5a21 100644 --- a/.github/workflows/pushes.yml +++ b/.github/workflows/pushes.yml @@ -21,9 +21,9 @@ jobs: ~/.m2/repository ~/.cache vendor/bundle - key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + key: push-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | - ${{ runner.os }}- + push-${{ runner.os }}- - name: Set up Ruby 2.7 uses: actions/setup-ruby@v1 with: @@ -78,9 +78,9 @@ jobs: ~/.m2/repository ~/.cache vendor/bundle - key: ${{ runner.os }}-coveralls-${{ hashFiles('**/pom.xml') }} + key: coveralls-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | - ${{ runner.os }}-coveralls- + coveralls-${{ runner.os }}- - name: Check Environment run: .ci/check-environment.sh shell: bash @@ -103,9 +103,9 @@ jobs: ~/.m2/repository ~/.cache vendor/bundle - key: ${{ runner.os }}-sonar-${{ hashFiles('**/pom.xml') }} + key: sonar-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | - ${{ runner.os }}-sonar- + sonar-${{ runner.os }}- - name: Check Environment run: .ci/check-environment.sh shell: bash diff --git a/.github/workflows/troubleshooting.yml b/.github/workflows/troubleshooting.yml index 8065541981..dd79aa8c3e 100644 --- a/.github/workflows/troubleshooting.yml +++ b/.github/workflows/troubleshooting.yml @@ -19,9 +19,9 @@ jobs: ~/.m2/repository ~/.cache vendor/bundle - key: ${{ runner.os }}-${{ hashFiles('**/pom.xml') }} + key: push-${{ runner.os }}-${{ hashFiles('**/pom.xml') }} restore-keys: | - ${{ runner.os }}- + push-${{ runner.os }}- - name: Set up Ruby 2.7 uses: actions/setup-ruby@v1 with: From ce999665b47f85a5325d75edc2f10bf036d46204 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Wed, 25 Nov 2020 14:16:45 +0100 Subject: [PATCH 235/299] [ci] Remove download_baseline, not needed anymore --- Dangerfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Dangerfile b/Dangerfile index 8781bc34a1..0665207264 100644 --- a/Dangerfile +++ b/Dangerfile @@ -25,7 +25,6 @@ def run_pmdtester begin @base_branch = ENV['PMD_CI_BRANCH'] @logger.info "Run against PR base #{@base_branch}" - download_baseline(@base_branch) @summary = PmdTester::Runner.new(get_args(@base_branch)).run unless Dir.exist?('target/reports/diff') @@ -41,7 +40,6 @@ def run_pmdtester unless ENV['PMD_CI_BRANCH'] == 'master' @base_branch = 'master' @logger.info "Run against #{@base_branch}" - download_baseline(@base_branch) @summary = PmdTester::Runner.new(get_args(@base_branch)).run # move the generated report out of the way From 9d518790881cfb376df778db9cbf948f4cf33e67 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Wed, 25 Nov 2020 15:58:35 +0100 Subject: [PATCH 236/299] Small fixes (since=7.0.0, unused ExpectedExeption) --- .../lang/java/rule/xpath/internal/BaseXPathFunctionTest.java | 2 +- .../java/rule/xpath/internal/GetModifiersFunctionsTest.java | 2 +- .../java/rule/xpath/internal/HasAnnotationXPathTest.java | 2 +- .../java/rule/xpath/internal/MatchesSignatureXPathTest.java | 2 +- .../lang/java/rule/xpath/internal/NodeIsFunctionTest.java | 2 +- .../pmd/lang/java/types/InvocationMatcherTest.java | 5 ----- 6 files changed, 5 insertions(+), 10 deletions(-) diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java index bf5e264856..86ae4e3389 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java @@ -24,7 +24,7 @@ import net.sourceforge.pmd.lang.rule.xpath.XPathVersion; /** * @author Clément Fournier - * @since 6.0.0 + * @since 7.0.0 */ public class BaseXPathFunctionTest extends BaseNonParserTest { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java index ab5afb58b6..37b67fd77b 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/GetModifiersFunctionsTest.java @@ -15,7 +15,7 @@ import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException.Phase; /** * @author Clément Fournier - * @since 6.0.0 + * @since 7.0.0 */ public class GetModifiersFunctionsTest extends BaseXPathFunctionTest { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java index fed206c337..3079fb7071 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/HasAnnotationXPathTest.java @@ -10,7 +10,7 @@ import net.sourceforge.pmd.Rule; /** * @author Clément Fournier - * @since 6.0.0 + * @since 7.0.0 */ public class HasAnnotationXPathTest extends BaseXPathFunctionTest { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java index 55eba4ef8e..29023cfcc8 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java @@ -10,7 +10,7 @@ import net.sourceforge.pmd.Rule; /** * @author Clément Fournier - * @since 6.0.0 + * @since 7.0.0 */ public class MatchesSignatureXPathTest extends BaseXPathFunctionTest { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java index a93edfd99a..97a2b9a69c 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/NodeIsFunctionTest.java @@ -15,7 +15,7 @@ import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException.Phase; /** * @author Clément Fournier - * @since 6.0.0 + * @since 7.0.0 */ public class NodeIsFunctionTest extends BaseXPathFunctionTest { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java index 7c088371ae..d6f72183d5 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/InvocationMatcherTest.java @@ -9,9 +9,7 @@ import static org.hamcrest.Matchers.equalTo; import org.hamcrest.MatcherAssert; import org.junit.Assert; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; @@ -20,9 +18,6 @@ import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest; public class InvocationMatcherTest extends BaseNonParserTest { - @Rule - public final ExpectedException expect = ExpectedException.none(); - @Test public void testSimpleMatcher() { From ee59732ec52d4d39a84af1db50154f5885c4aa7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 25 Nov 2020 18:11:31 +0100 Subject: [PATCH 237/299] [java-xpath] Cleanups after #2917 * Improve docs * Add missing tests for XPath functions * TypeIsFunction has been replaced with BaseContextNodeTestFun, so this deletes the file getCommentOn is still untested. I wonder whether we really need it, also, it's inefficient and not specified at all (because it's unused). --- .../xpath/internal/BaseRewrittenFunction.java | 25 ++++++- .../internal/MatchesSignatureFunction.java | 4 +- .../rule/xpath/internal/NodeIsFunction.java | 6 +- .../rule/xpath/internal/TypeIsFunction.java | 75 ------------------- .../internal/MatchesSignatureXPathTest.java | 13 ++++ .../xpath/internal/TypeIsFunctionTest.java | 48 ++++++++++++ 6 files changed, 88 insertions(+), 83 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunction.java create mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunctionTest.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java index 1583d95577..f2f1e4c768 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseRewrittenFunction.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; import static net.sourceforge.pmd.lang.java.rule.xpath.internal.BaseContextNodeTestFun.SINGLE_STRING_SEQ; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode; import net.sf.saxon.expr.Expression; @@ -24,8 +23,13 @@ import net.sf.saxon.value.SequenceType; /** * A context node test function that may parse its string argument early * if it is a string literal. + * + * @param Type of state into which the argument is parsed + * @param Type of node the function applies. The function will return + * false for other kinds of node. */ -abstract class BaseRewrittenFunction extends BaseJavaXPathFunction { +// TODO could move that up to pmd-core +abstract class BaseRewrittenFunction extends BaseJavaXPathFunction { private final Class contextNodeType; @@ -50,8 +54,23 @@ abstract class BaseRewrittenFunction extends BaseJavaXPat } - protected abstract S parseArgument(String constantArg) throws XPathException; + /** + * Parse the argument into the state. This is called at build time + * if the arg is constant, otherwise it's anyway called before {@link #matches(Node, String, Object, boolean)} + * is called. + */ + protected abstract S parseArgument(String arg) throws XPathException; + /** + * Compute the result of the function. + * + * @param contextNode Context node + * @param arg Value of the argument + * @param parsedArg Result of {@link #parseArgument(String)} on the argument + * @param isConstant Whether the argument is constant (it was parsed in all cases) + * + * @return Whether the function matches + */ protected abstract boolean matches(N contextNode, String arg, S parsedArg, boolean isConstant) throws XPathException; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java index 4bc4baa301..a2a27f0224 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureFunction.java @@ -23,9 +23,9 @@ public final class MatchesSignatureFunction extends BaseRewrittenFunction, JavaNo } @Override - protected Class parseArgument(String constantArg) throws XPathException { + protected Class parseArgument(String arg) throws XPathException { try { - return Class.forName("net.sourceforge.pmd.lang.java.ast.AST" + constantArg); + return Class.forName("net.sourceforge.pmd.lang.java.ast.AST" + arg); } catch (ClassNotFoundException e) { - throw new XPathException("No class named AST" + constantArg); + throw new XPathException("No class named AST" + arg); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunction.java deleted file mode 100644 index a339bd4b43..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunction.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.xpath.internal; - -import java.util.function.BiPredicate; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.TypeNode; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; -import net.sourceforge.pmd.lang.rule.xpath.internal.AstElementNode; - -import net.sf.saxon.expr.XPathContext; -import net.sf.saxon.lib.ExtensionFunctionCall; -import net.sf.saxon.om.Sequence; -import net.sf.saxon.trans.XPathException; -import net.sf.saxon.value.BooleanValue; -import net.sf.saxon.value.SequenceType; - - -/** - * XPath function {@code pmd-java:typeIs(typeName as xs:string) as xs:boolean} - * and {@code typeIsExactly}. - * - *

Example XPath 2.0: {@code //ClassOrInterfaceType[pmd-java:typeIs('java.lang.String')]} - * - *

Returns true if the type of the node matches, false otherwise. - */ -public final class TypeIsFunction extends BaseJavaXPathFunction { - - public static final TypeIsFunction TYPE_IS_EXACTLY = new TypeIsFunction("typeIsExactly", TypeTestUtil::isExactlyA); - public static final TypeIsFunction TYPE_IS = new TypeIsFunction("typeIs", TypeTestUtil::isA); - - private final BiPredicate checker; - - private TypeIsFunction(String localName, BiPredicate checker) { - super(localName); - this.checker = checker; - } - - @Override - public SequenceType[] getArgumentTypes() { - return new SequenceType[] {SequenceType.SINGLE_STRING}; - } - - - @Override - public SequenceType getResultType(SequenceType[] suppliedArgumentTypes) { - return SequenceType.SINGLE_BOOLEAN; - } - - - @Override - public boolean dependsOnFocus() { - return true; - } - - @Override - public ExtensionFunctionCall makeCallExpression() { - return new ExtensionFunctionCall() { - @Override - public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException { - Node contextNode = ((AstElementNode) context.getContextItem()).getUnderlyingNode(); - String fullTypeName = arguments[0].head().getStringValue(); - - if (contextNode instanceof TypeNode) { - return BooleanValue.get(checker.test(fullTypeName, (TypeNode) contextNode)); - } else { - throw new IllegalArgumentException("typeIs function may only be called on a TypeNode."); - } - } - }; - } -} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java index 29023cfcc8..edc812faaa 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/MatchesSignatureXPathTest.java @@ -4,9 +4,14 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; +import static org.junit.Assert.assertEquals; + +import org.junit.Assert; import org.junit.Test; import net.sourceforge.pmd.Rule; +import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException; +import net.sourceforge.pmd.lang.rule.xpath.PmdXPathException.Phase; /** * @author Clément Fournier @@ -53,5 +58,13 @@ public class MatchesSignatureXPathTest extends BaseXPathFunctionTest { assertFinds(rule, 0, "enum O {; { \"\".substring(1, 2); this.foo(1, 'c');} void foo(int a, int b) {} }"); } + @Test + public void testMatchInvalidSig() { + Rule rule = makeXpathRuleFromXPath("//*[pmd-java:matchesSig('_#')]"); + + PmdXPathException e = Assert.assertThrows(PmdXPathException.class, rule::getTargetSelector); + assertEquals(Phase.INITIALIZATION, e.getPhase()); + } + } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunctionTest.java new file mode 100644 index 0000000000..2f96c75897 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/TypeIsFunctionTest.java @@ -0,0 +1,48 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.rule.xpath.internal; + +import org.junit.Test; + +import net.sourceforge.pmd.Rule; + +/** + * @author Clément Fournier + * @since 7.0.0 + */ +public class TypeIsFunctionTest extends BaseXPathFunctionTest { + + + @Test + public void testHasAnnotation() { + Rule rule = makeXpathRuleFromXPath("//Annotation[pmd-java:typeIs('java.lang.Override')]"); + assertFinds(rule, 1, "interface O { @Override void foo(); }"); + } + + + @Test + public void testHasAnnotationNonQual() { + Rule rule = makeXpathRuleFromXPath("//Annotation[pmd-java:typeIs('Override')]"); + + //does not match + assertFinds(rule, 0, "interface O { @Override void foo(); }"); + } + + @Test + public void testTypeIsArray() { + Rule rule = makeXpathRuleFromXPath("//*[pmd-java:typeIs('int[]')]"); + + // ArrayType + VariableDeclaratorId + assertFinds(rule, 2, "class K { int[] i; }"); + } + + @Test + public void testWrongTypeReturnsFalse() { + Rule rule = makeXpathRuleFromXPath("//ClassOrInterfaceBody[pmd-java:typeIs('java.lang.Override')]"); + + assertFinds(rule, 0, "interface O { @Override void foo(); }"); + } + +} From dd625ed18df9265c23b62b945856de21246f902a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Wed, 25 Nov 2020 23:30:32 +0100 Subject: [PATCH 238/299] Remove unused class --- .../pmd/lang/java/rule/regex/RegexHelper.java | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/regex/RegexHelper.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/regex/RegexHelper.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/regex/RegexHelper.java deleted file mode 100644 index a444b61c48..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/regex/RegexHelper.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.regex; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import net.sourceforge.pmd.annotation.InternalApi; - -/** - * A simple helper class to regroup a bunch of method generally used by rules - * using regex. - * - * @author Romain PELISSE, belaran@gmail.com - * @deprecated Internal API - */ -@Deprecated -@InternalApi -public final class RegexHelper { - - /** - * Default private empty constructors - */ - private RegexHelper() { - } - - /** - * Compiles a list of regex into a list of patterns. - * - * @param list - * the regex list - * @return the pattern list - */ - public static List compilePatternsFromList(List list) { - List patterns; - if (list != null && !list.isEmpty()) { - patterns = new ArrayList<>(list.size()); - for (String stringPattern : list) { - if (stringPattern != null && !"".equals(stringPattern)) { - patterns.add(Pattern.compile(stringPattern)); - } - } - } else { - patterns = new ArrayList<>(0); - } - return patterns; - } - - /** - * Simple commodity method (also designed to increase readability of source - * code, and to decrease import in the calling class). Provide a pattern and - * a subject, it'll do the proper matching. - * - * @param pattern - * a compiled regex pattern - * @param subject - * a String to match - * @return {@code true} if there is a match; {@code false} otherwise - */ - public static boolean isMatch(Pattern pattern, String subject) { - if (subject != null && !"".equals(subject)) { - Matcher matcher = pattern.matcher(subject); - if (matcher.find()) { - return true; - } - } - return false; - } - -} From 8d8d4544a704da1693e0b887d4ae4e03e05adcc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 28 Nov 2020 14:26:43 +0100 Subject: [PATCH 239/299] Update release notes Refs #2940 --- 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 5a2156f2b0..64cadb6560 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -24,6 +24,7 @@ This is a {{ site.pmd.release_type }} release. * [#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 + * [#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 From c47a0dc51a4d3bc0ca1f5447c0f34752f306c050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 28 Nov 2020 15:47:37 +0100 Subject: [PATCH 240/299] Update CompareObjectsWithEquals for #2934 --- .../CompareObjectsWithEqualsRule.java | 86 ------------------- .../resources/category/java/errorprone.xml | 13 +-- .../xml/CompareObjectsWithEquals.xml | 7 +- 3 files changed, 12 insertions(+), 94 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java deleted file mode 100644 index a2922fcc3c..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/CompareObjectsWithEqualsRule.java +++ /dev/null @@ -1,86 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.errorprone; - -import org.checkerframework.checker.nullness.qual.NonNull; - -import net.sourceforge.pmd.lang.java.ast.ASTArrayAllocation; -import net.sourceforge.pmd.lang.java.ast.ASTBodyDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall; -import net.sourceforge.pmd.lang.java.ast.ASTExpression; -import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; -import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; -import net.sourceforge.pmd.lang.java.ast.ASTThisExpression; -import net.sourceforge.pmd.lang.java.ast.BinaryOp; -import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -import net.sourceforge.pmd.lang.java.types.TypeTestUtil; - -public class CompareObjectsWithEqualsRule extends AbstractJavaRulechainRule { - - public CompareObjectsWithEqualsRule() { - super(ASTInfixExpression.class); - } - - /** Indicate whether this node is allocating a new object. */ - private boolean isAllocation(ASTExpression n) { - return n instanceof ASTConstructorCall || n instanceof ASTArrayAllocation; - } - - @Override - public Object visit(ASTInfixExpression node, Object data) { - if (!node.getOperator().hasSamePrecedenceAs(BinaryOp.EQ)) { - return data; - } - ASTExpression left = node.getLeftOperand(); - ASTExpression right = node.getRightOperand(); - - // If either side is allocating a new object, there's no way an - // equals expression is correct - if (isAllocation(left) || isAllocation(right)) { - addViolation(data, node); - return data; - } - - if (!isAllowedIdentityComparison(node)) { - addViolation(data, node); - } - - return data; - } - - private boolean isIgnoredType(ASTExpression left) { - return left.getTypeMirror().isPrimitive() - || TypeTestUtil.isA(Enum.class, left) - || TypeTestUtil.isA(Class.class, left); - } - - private boolean isAllowedIdentityComparison(ASTInfixExpression infix) { - ASTBodyDeclaration enclosing = infix.ancestors(ASTBodyDeclaration.class).first(); - - ASTExpression left = infix.getLeftOperand(); - ASTExpression right = infix.getRightOperand(); - if (isEqualsMethod(enclosing) && (isThisExpr(left) || isThisExpr(right))) { - // this == _ is allowed in equals methods - return true; - } else { - return isIgnoredType(left) || isIgnoredType(right); - } - } - - private static boolean isThisExpr(@NonNull ASTExpression expr) { - return expr instanceof ASTThisExpression; - } - - private static boolean isEqualsMethod(ASTBodyDeclaration first) { - if (first instanceof ASTMethodDeclaration) { - ASTMethodDeclaration m = (ASTMethodDeclaration) first; - return "equals".equals(m.getName()) - && m.getArity() == 1 - && TypeTestUtil.isExactlyA(Object.class, m.getFormalParameters().get(0)); - } - return false; - } - -} diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index f051de076c..c3aec21c3e 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1098,16 +1098,17 @@ Use equals() to compare object references; avoid comparing them with ==. - - + [not(ancestor::MethodDeclaration[1][@Name = "equals"])] + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index fd0530525c..70b7b3eedf 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -53,8 +53,10 @@ public class Foo { 1 @@ -362,7 +364,8 @@ public class ClassWithFields { private Object b; void test(Object o) { - if (this == o) { } // should be allowed, since this is a often used pattern in Object::equals. + // conflict between #2899 and #2934 + // if (this == o) { } // should be allowed, since this is a often used pattern in Object::equals. // comparing class instances is ok if (o.getClass() == a.getClass()) { } if (this.getClass() == this.a.getClass()) { } From f2faf0996cc8578fba8278fc1485fe705d897bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 30 Nov 2020 18:00:37 +0100 Subject: [PATCH 241/299] Add doc in cpd.md --- docs/pages/pmd/userdocs/cpd/cpd.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/pages/pmd/userdocs/cpd/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md index c97bbdfbfd..d5bfa0fd44 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd.md +++ b/docs/pages/pmd/userdocs/cpd/cpd.md @@ -116,6 +116,11 @@ Novice as much as advanced readers may want to [read on on Refactoring Guru](htt default="false" languages="Java" %} + {% include custom/cli_option_row.html options="--ignore-literal-sequences" + description="Ignore sequences of literals (common e.g. in list initializers)" + default="false" + languages="C#" + %} {% include custom/cli_option_row.html options="--ignore-usings" description="Ignore `using` directives in C# when comparing text" default="false" From e999010fb70e1e510ed5f31d3aadd7e6cdf767cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 30 Nov 2020 18:00:42 +0100 Subject: [PATCH 242/299] Update release notes, refs #2945 --- docs/pages/release_notes.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index c39aa66d1b..fde03be323 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 +##### CPD + +* The C# module now supports the new option `--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 `CPD-ON` and `CPD-OFF` comment pairs. See [#2929](https://github.com/pmd/pmd/pull/2929) + ### Fixed Issues * core @@ -63,5 +69,6 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr * [#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) {% endtocmaker %} From 5780739c454c08bd6cc440ad3c5f933d1106a951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 30 Nov 2020 18:02:26 +0100 Subject: [PATCH 243/299] Update CPD md page for #2929 --- docs/pages/pmd/userdocs/cpd/cpd.md | 2 +- docs/pages/release_notes.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/pmd/userdocs/cpd/cpd.md b/docs/pages/pmd/userdocs/cpd/cpd.md index d5bfa0fd44..8d94294926 100644 --- a/docs/pages/pmd/userdocs/cpd/cpd.md +++ b/docs/pages/pmd/userdocs/cpd/cpd.md @@ -375,7 +375,7 @@ Here's a screenshot of CPD after running on the JDK 8 java.lang package: ## Suppression Arbitrary blocks of code can be ignored through comments on **Java**, **C/C++**, **Dart**, **Go**, **Javascript**, -**Kotlin**, **Lua**, **Matlab**, **Objective-C**, **PL/SQL**, **Python**, **Swift** and **C#** by including the keywords `CPD-OFF` and `CPD-ON`. +**Kotlin**, **Lua**, **Matlab**, **Objective-C**, **PL/SQL**, **Python**, **Scala**, **Swift** and **C#** by including the keywords `CPD-OFF` and `CPD-ON`. ```java public Object someParameterizedFactoryMethod(int x) throws Exception { diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index fde03be323..ae00901050 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,7 +18,7 @@ This is a {{ site.pmd.release_type }} release. * The C# module now supports the new option `--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 `CPD-ON` and `CPD-OFF` comment pairs. See [#2929](https://github.com/pmd/pmd/pull/2929) +* 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) ### Fixed Issues From 968ff53b7c7c7c485650806067d889f5c3291504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Mon, 30 Nov 2020 18:05:30 +0100 Subject: [PATCH 244/299] Add links --- docs/pages/release_notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index ae00901050..cf766b7686 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -16,7 +16,7 @@ This is a {{ site.pmd.release_type }} release. ##### CPD -* The C# module now supports the new option `--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 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) From fe67ac789a0deb50ec0d349b05fa5ffb34253246 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 1 Dec 2020 15:53:12 +0100 Subject: [PATCH 245/299] Update C# test references End column number is exclusive in pmd 7. Refs #2945 --- .../lang/cs/cpd/testdata/listOfNumbers.txt | 668 +++++++++--------- .../cs/cpd/testdata/listOfNumbers_ignored.txt | 64 +- 2 files changed, 366 insertions(+), 366 deletions(-) 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..f5024fc29c 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 @@ -1,344 +1,344 @@ [Image] or [Truncated image[ Bcol Ecol L1 - [using] 1 5 - [System] 7 12 - [;] 13 13 + [using] 1 6 + [System] 7 13 + [;] 13 14 L2 - [using] 1 5 - [System] 7 12 - [.] 13 13 - [Collections] 14 24 - [;] 25 25 + [using] 1 6 + [System] 7 13 + [.] 13 14 + [Collections] 14 25 + [;] 25 26 L3 - [using] 1 5 - [System] 7 12 - [.] 13 13 - [Collections] 14 24 - [.] 25 25 - [Generic] 26 32 - [;] 33 33 + [using] 1 6 + [System] 7 13 + [.] 13 14 + [Collections] 14 25 + [.] 25 26 + [Generic] 26 33 + [;] 33 34 L4 - [public] 1 6 - [class] 8 12 - [LongLists] 14 22 - [{] 24 24 + [public] 1 7 + [class] 8 13 + [LongLists] 14 23 + [{] 24 25 L5 - [List] 5 8 - [<] 9 9 - [byte] 10 13 - [>] 14 14 - [l] 16 16 - [=] 18 18 - [new] 20 22 - [List] 24 27 - [<] 28 28 - [byte] 29 32 - [>] 33 33 - [{] 35 35 + [List] 5 9 + [<] 9 10 + [byte] 10 14 + [>] 14 15 + [l] 16 17 + [=] 18 19 + [new] 20 23 + [List] 24 28 + [<] 28 29 + [byte] 29 33 + [>] 33 34 + [{] 35 36 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] 6 7 + [,] 7 8 + [0] 8 9 + [,] 9 10 + [0] 10 11 + [,] 11 12 + [0] 12 13 + [,] 13 14 + [0] 14 15 + [,] 15 16 + [0] 16 17 + [,] 17 18 + [0] 18 19 + [,] 19 20 + [0] 20 21 + [,] 21 22 + [0] 22 23 + [,] 23 24 + [0] 24 25 + [,] 25 26 + [0] 26 27 + [,] 27 28 + [0] 28 29 + [,] 29 30 + [0] 30 31 + [,] 31 32 + [0] 32 33 + [,] 33 34 + [0] 34 35 + [,] 35 36 + [0] 36 37 + [,] 37 38 + [0] 38 39 + [,] 39 40 + [0] 40 41 + [,] 41 42 + [0] 42 43 + [,] 43 44 + [0] 44 45 + [,] 45 46 + [0] 46 47 + [,] 47 48 + [0] 48 49 + [,] 49 50 + [0] 50 51 + [,] 51 52 + [0] 52 53 + [,] 53 54 + [0] 54 55 + [,] 55 56 + [0] 56 57 + [,] 57 58 + [0] 58 59 + [,] 59 60 + [0] 60 61 + [,] 61 62 + [0] 62 63 + [,] 63 64 + [0] 64 65 + [,] 65 66 + [0] 66 67 + [,] 67 68 + [0] 68 69 + [,] 69 70 + [0] 70 71 + [,] 71 72 + [0] 72 73 + [,] 73 74 + [0] 74 75 + [,] 75 76 + [0] 76 77 + [,] 77 78 + [0] 78 79 + [,] 79 80 + [0] 80 81 + [,] 81 82 + [0] 82 83 + [,] 83 84 + [0] 84 85 + [,] 85 86 + [0] 86 87 + [,] 87 88 + [0] 88 89 + [,] 89 90 + [0] 90 91 + [,] 91 92 + [0] 92 93 + [,] 93 94 + [0] 94 95 + [,] 95 96 + [0] 96 97 + [,] 97 98 + [0] 98 99 + [,] 99 100 + [0] 100 101 + [,] 101 102 + [0] 102 103 + [,] 103 104 + [0] 104 105 + [,] 105 106 + [0] 106 107 + [,] 107 108 + [0] 108 109 + [,] 109 110 + [0] 110 111 + [,] 111 112 + [0] 112 113 + [,] 113 114 + [0] 114 115 + [,] 115 116 + [0] 116 117 + [,] 117 118 + [0] 118 119 + [,] 119 120 + [0] 120 121 + [,] 121 122 + [0] 122 123 + [,] 123 124 + [0] 124 125 + [,] 125 126 + [0] 126 127 + [,] 127 128 + [0] 128 129 + [,] 129 130 + [0] 130 131 + [,] 131 132 + [0] 132 133 + [,] 133 134 + [0] 134 135 + [,] 135 136 + [0] 136 137 + [,] 137 138 + [0] 138 139 + [,] 139 140 + [0] 140 141 + [,] 141 142 + [0] 142 143 + [,] 143 144 + [0] 144 145 + [,] 145 146 + [0] 146 147 + [,] 147 148 + [0] 148 149 + [,] 149 150 + [0] 150 151 + [,] 151 152 + [0] 152 153 + [,] 153 154 + [0] 154 155 + [,] 155 156 + [0] 156 157 + [,] 157 158 + [0] 158 159 + [,] 159 160 + [0] 160 161 + [,] 161 162 + [0] 162 163 + [,] 163 164 + [0] 164 165 + [,] 165 166 + [0] 166 167 + [,] 167 168 + [0] 168 169 + [,] 169 170 + [0] 170 171 + [,] 171 172 + [0] 172 173 + [,] 173 174 + [0] 174 175 + [,] 175 176 + [0] 176 177 + [,] 177 178 + [0] 178 179 + [,] 179 180 + [0] 180 181 + [,] 181 182 + [0] 182 183 + [,] 183 184 + [0] 184 185 + [,] 185 186 + [0] 186 187 + [,] 187 188 + [0] 188 189 + [,] 189 190 + [0] 190 191 + [,] 191 192 + [0] 192 193 + [,] 193 194 + [0] 194 195 + [,] 195 196 + [0] 196 197 + [,] 197 198 + [0] 198 199 + [,] 199 200 + [0] 200 201 + [,] 201 202 + [0] 202 203 + [,] 203 204 + [0] 204 205 + [,] 205 206 + [0] 206 207 + [,] 207 208 + [0] 208 209 + [,] 209 210 + [0] 210 211 + [,] 211 212 + [0] 212 213 + [,] 213 214 + [0] 214 215 + [,] 215 216 + [0] 216 217 + [,] 217 218 + [0] 218 219 + [,] 219 220 + [0] 220 221 + [,] 221 222 + [0] 222 223 + [,] 223 224 + [0] 224 225 + [,] 225 226 + [0] 226 227 + [,] 227 228 + [0] 228 229 + [,] 229 230 + [0] 230 231 + [,] 231 232 + [0] 232 233 + [,] 233 234 + [0] 234 235 + [,] 235 236 + [0] 236 237 + [,] 237 238 + [0] 238 239 + [,] 239 240 + [0] 240 241 + [,] 241 242 + [0] 242 243 + [,] 243 244 + [0] 244 245 + [,] 245 246 + [0] 246 247 + [,] 247 248 + [0] 248 249 + [,] 249 250 + [0] 250 251 + [,] 251 252 + [0] 252 253 + [,] 253 254 + [0] 254 255 + [,] 255 256 + [0] 256 257 + [,] 257 258 + [0] 258 259 + [,] 259 260 + [0] 260 261 + [,] 261 262 + [0] 262 263 + [,] 263 264 + [0] 264 265 + [,] 265 266 + [0] 266 267 + [,] 267 268 + [0] 268 269 + [,] 269 270 + [0] 270 271 + [,] 271 272 + [0] 272 273 + [,] 273 274 + [0] 274 275 + [,] 275 276 + [0] 276 277 + [,] 277 278 + [0] 278 279 + [,] 279 280 + [0] 280 281 + [,] 281 282 + [0] 282 283 + [,] 283 284 + [0] 284 285 + [,] 285 286 + [0] 286 287 + [,] 287 288 + [0] 288 289 + [,] 289 290 + [0] 290 291 + [,] 291 292 + [0] 292 293 + [,] 293 294 + [0] 294 295 + [,] 295 296 + [0] 296 297 + [,] 297 298 + [0] 298 299 + [,] 299 300 + [0] 300 301 + [,] 301 302 + [0] 302 303 + [,] 303 304 + [0] 304 305 + [,] 305 306 L7 - [}] 5 5 - [;] 6 6 + [}] 5 6 + [;] 6 7 L8 - [}] 1 1 + [}] 1 2 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..811acd84ec 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 @@ -1,41 +1,41 @@ [Image] or [Truncated image[ Bcol Ecol L1 - [using] 1 5 - [System] 7 12 - [;] 13 13 + [using] 1 6 + [System] 7 13 + [;] 13 14 L2 - [using] 1 5 - [System] 7 12 - [.] 13 13 - [Collections] 14 24 - [;] 25 25 + [using] 1 6 + [System] 7 13 + [.] 13 14 + [Collections] 14 25 + [;] 25 26 L3 - [using] 1 5 - [System] 7 12 - [.] 13 13 - [Collections] 14 24 - [.] 25 25 - [Generic] 26 32 - [;] 33 33 + [using] 1 6 + [System] 7 13 + [.] 13 14 + [Collections] 14 25 + [.] 25 26 + [Generic] 26 33 + [;] 33 34 L4 - [public] 1 6 - [class] 8 12 - [LongLists] 14 22 - [{] 24 24 + [public] 1 7 + [class] 8 13 + [LongLists] 14 23 + [{] 24 25 L5 - [List] 5 8 - [<] 9 9 - [byte] 10 13 - [>] 14 14 - [l] 16 16 - [=] 18 18 - [new] 20 22 - [List] 24 27 - [<] 28 28 - [byte] 29 32 - [>] 33 33 + [List] 5 9 + [<] 9 10 + [byte] 10 14 + [>] 14 15 + [l] 16 17 + [=] 18 19 + [new] 20 23 + [List] 24 28 + [<] 28 29 + [byte] 29 33 + [>] 33 34 L7 - [;] 6 6 + [;] 6 7 L8 - [}] 1 1 + [}] 1 2 EOF From 69d6df044a5940a597fa6337aa942e6c0f85b64b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Tue, 1 Dec 2020 16:07:33 +0100 Subject: [PATCH 246/299] Ignore isFindBoundary NoAttribute cannot be inherited... --- .../rule/xpath/impl/AttributeAxisIterator.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIterator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIterator.java index 2599a44892..b2a9bb633a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIterator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIterator.java @@ -39,7 +39,20 @@ public class AttributeAxisIterator implements Iterator { Long.TYPE, Character.TYPE, Float.TYPE)); private static final Set FILTERED_OUT_NAMES - = new HashSet<>(Arrays.asList("toString", "getNumChildren", "getIndexInParent", "getParent", "getClass", "getRuleIndex", "getXPathNodeName", "altNumber", "toStringTree", "getTypeNameNode", "hashCode", "getImportedNameNode", "getScope")); + = new HashSet<>(Arrays.asList("toString", + "getNumChildren", + "getIndexInParent", + "getParent", + "getClass", + "isFindBoundary", + "getRuleIndex", + "getXPathNodeName", + "altNumber", + "toStringTree", + "getTypeNameNode", + "hashCode", + "getImportedNameNode", + "getScope")); /* Iteration variables */ private final Iterator iterator; From 899937733146e56e6fbd5469afd064a5a6068934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 3 Dec 2020 12:54:10 +0100 Subject: [PATCH 247/299] Fix tests --- .../sourceforge/pmd/util/CollectionUtil.java | 17 ++++++++++ .../xpath/impl/AttributeAxisIteratorTest.java | 32 ++++++++----------- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java index 422f2f0dd6..218ddf5a76 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/CollectionUtil.java @@ -294,6 +294,23 @@ public final class CollectionUtil { return newM; } + /** + * Returns an unmodifiable set containing the set union of the collection, + * and the new elements. + */ + @SafeVarargs + @SuppressWarnings("unchecked") + public static Set setUnion(Collection set, V first, V... newElements) { + if (set instanceof PSet) { + return ((PSet) set).plus(first).plusAll(Arrays.asList(newElements)); + } + Set newSet = new LinkedHashSet<>(set.size() + 1 + newElements.length); + newSet.addAll(set); + newSet.add(first); + Collections.addAll(newSet, newElements); + return Collections.unmodifiableSet(newSet); + } + /** * Returns a map associating each key in the first list to its diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIteratorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIteratorTest.java index dc0ca2f6f6..ca9f6d8811 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIteratorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/impl/AttributeAxisIteratorTest.java @@ -5,21 +5,22 @@ package net.sourceforge.pmd.lang.rule.xpath.impl; +import static net.sourceforge.pmd.util.CollectionUtil.setOf; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.junit.Test; import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.xpath.Attribute; +import net.sourceforge.pmd.util.CollectionUtil; /** @@ -27,6 +28,8 @@ import net.sourceforge.pmd.lang.rule.xpath.Attribute; */ public class AttributeAxisIteratorTest { + private static final Set DEFAULT_ATTRS = setOf("BeginColumn", "BeginLine", "Image", "EndColumn", "EndLine"); + /** * Test hasNext and next. */ @@ -36,14 +39,8 @@ public class AttributeAxisIteratorTest { dummyNode.setCoords(1, 1, 2, 2); AttributeAxisIterator it = new AttributeAxisIterator(dummyNode); - Map atts = toMap(it); - assertEquals(6, atts.size()); - assertTrue(atts.containsKey("BeginColumn")); - assertTrue(atts.containsKey("BeginLine")); - assertTrue(atts.containsKey("FindBoundary")); - assertTrue(atts.containsKey("Image")); - assertTrue(atts.containsKey("EndColumn")); - assertTrue(atts.containsKey("EndLine")); + + assertEquals(DEFAULT_ATTRS, toMap(it).keySet()); } @Test @@ -51,21 +48,20 @@ public class AttributeAxisIteratorTest { DummyNodeWithEnum dummyNode = new DummyNodeWithEnum(); AttributeAxisIterator it = new AttributeAxisIterator(dummyNode); - Map atts = toMap(it); - assertEquals(7, atts.size()); - assertTrue(atts.containsKey("Enum")); - assertEquals(DummyNodeWithEnum.MyEnum.FOO, atts.get("Enum").getValue()); + + Set expected = CollectionUtil.setUnion(DEFAULT_ATTRS, "Enum"); + + assertEquals(expected, toMap(it).keySet()); } @Test public void testAttributeAxisIteratorWithList() { + // list attributes are not supported anymore DummyNodeWithList dummyNode = new DummyNodeWithList(); AttributeAxisIterator it = new AttributeAxisIterator(dummyNode); - Map atts = toMap(it); - assertEquals(6, atts.size()); - assertFalse(atts.containsKey("List")); - assertFalse(atts.containsKey("NodeList")); + + assertEquals(DEFAULT_ATTRS, toMap(it).keySet()); } private Map toMap(AttributeAxisIterator it) { From fcbccbce43fe9059b50d4f81c1b6bfb3e9f43e38 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 4 Dec 2020 17:34:09 +0100 Subject: [PATCH 248/299] [ci] Fix do-release.sh script --- .ci/build.sh | 1 + do-release.sh | 9 ++++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.ci/build.sh b/.ci/build.sh index 138f4041de..d6475b70a1 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -187,6 +187,7 @@ function pmd_ci_build_and_upload_doc() { gh_release_updateRelease "$GH_RELEASE" "$release_name" "$rendered_release_notes" sourceforge_uploadReleaseNotes "${VERSION}" "${rendered_release_notes}" + # updates https://pmd.github.io/latest/ and https://pmd.github.io/pmd-${VERSION} publish_release_documentation_github sourceforge_rsyncSnapshotDocumentation "${VERSION}" "pmd-${VERSION}" fi diff --git a/do-release.sh b/do-release.sh index fa8617e1a4..bff8d1f6aa 100755 --- a/do-release.sh +++ b/do-release.sh @@ -24,8 +24,7 @@ echo "-------------------------------------------" echo "Releasing PMD" echo "-------------------------------------------" -# see also https://gist.github.com/pdunnavant/4743895 -CURRENT_VERSION=$(./mvnw -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive org.codehaus.mojo:exec-maven-plugin:3.0.0:exec) +CURRENT_VERSION=$(./mvnw org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout) RELEASE_VERSION=${CURRENT_VERSION%-SNAPSHOT} MAJOR=$(echo $RELEASE_VERSION | cut -d . -f 1) MINOR=$(echo $RELEASE_VERSION | cut -d . -f 2) @@ -128,7 +127,7 @@ bundle install export RELEASE_NOTES_POST="_posts/$(date -u +%Y-%m-%d)-PMD-${RELEASE_VERSION}.md" echo "Generating ../pmd.github.io/${RELEASE_NOTES_POST}..." -NEW_RELEASE_NOTES=$(bundle exec .travis/render_release_notes.rb docs/pages/release_notes.md | tail -n +6) +NEW_RELEASE_NOTES=$(bundle exec .ci/render_release_notes.rb docs/pages/release_notes.md | tail -n +6) cat > ../pmd.github.io/${RELEASE_NOTES_POST} <" +echo "Tag has been pushed.... now check github actions: " echo echo echo "Press enter to continue..." @@ -228,7 +227,7 @@ echo echo echo "Verify the new release on github: " echo -echo "* Wait until the new version is synced to maven central and appears in as latest version in" +echo "* Wait until the new version is synced to maven central and appears as latest version in" echo " ." echo "* Submit news to SF on page. Use same text as in the email below." echo "* Send out an announcement mail to the mailing list:" From 7f299c4f6c3aac04fe0b6ae91f1d8afef40cc536 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 4 Dec 2020 18:21:11 +0100 Subject: [PATCH 249/299] [java] CompareObjectsWithEquals: only allow this with equals (#2934) --- pmd-java/src/main/resources/category/java/errorprone.xml | 5 ++++- .../rule/errorprone/xml/CompareObjectsWithEquals.xml | 9 +++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/errorprone.xml b/pmd-java/src/main/resources/category/java/errorprone.xml index 2e02d7e2f3..ae48bb363b 100644 --- a/pmd-java/src/main/resources/category/java/errorprone.xml +++ b/pmd-java/src/main/resources/category/java/errorprone.xml @@ -1114,7 +1114,10 @@ Use equals() to compare object references; avoid comparing them with ==. [not(pmd-java:typeIs('java.lang.Enum'))] [not(pmd-java:typeIs('java.lang.Class'))]) = 2 ] - [not(PrimaryExpression[PrimaryPrefix/@ThisModifier = true()][not(PrimarySuffix)])] + [not(PrimaryExpression[PrimaryPrefix/@ThisModifier = true()] + [not(PrimarySuffix)] + [ancestor::MethodDeclaration[@Name = 'equals']]) + ] ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml index 3a4b2dc453..893433e2e4 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/CompareObjectsWithEquals.xml @@ -294,7 +294,8 @@ public class ClassWithFields { #2934 this and class should be ignored - 0 + 1 + 16 From 5fffaa90f522a68b23ce93945f7bf9df4f24ae74 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 5 Dec 2020 15:09:11 +0100 Subject: [PATCH 250/299] [ci] Update pmdtester to 1.1.0 --- Gemfile | 4 ++-- Gemfile.lock | 20 +++++++++----------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Gemfile b/Gemfile index 8d17050419..b8861eb9c3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,9 +1,9 @@ source 'https://rubygems.org/' # bleeding edge from git -gem 'pmdtester', :git => 'https://github.com/pmd/pmd-regression-tester.git' +#gem 'pmdtester', :git => 'https://github.com/pmd/pmd-regression-tester.git' -#gem 'pmdtester', '~> 1.0' +gem 'pmdtester', '~> 1.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 0b12bef0a3..9bdbcf639f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,13 +1,3 @@ -GIT - remote: https://github.com/pmd/pmd-regression-tester.git - revision: ab54e3494917c9adf7cc236dddfee58094c2ed8a - specs: - pmdtester (1.1.0.pre.SNAPSHOT) - differ (~> 0.1) - nokogiri (~> 1.8) - rufus-scheduler (~> 3.5) - slop (~> 4.6) - GEM remote: https://rubygems.org/ specs: @@ -48,6 +38,7 @@ GEM rchardet (~> 1.8) kramdown (1.17.0) liquid (4.0.3) + logger-colors (1.0.0) mini_portile2 (2.4.0) multipart-post (2.1.1) nap (1.1.0) @@ -58,6 +49,13 @@ GEM faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) open4 (1.3.4) + pmdtester (1.1.0) + differ (~> 0.1) + liquid (>= 4.0) + logger-colors (~> 1.0) + nokogiri (~> 1.8) + rufus-scheduler (~> 3.5) + slop (~> 4.6) public_suffix (4.0.6) raabro (1.4.0) rchardet (1.8.0) @@ -81,7 +79,7 @@ PLATFORMS DEPENDENCIES danger (~> 5.6, >= 5.6) liquid (>= 4.0.0) - pmdtester! + pmdtester (~> 1.1) rouge (>= 1.7, < 4) safe_yaml (>= 1.0) From f805b858504191c38c752578f8aac544f917f6fd Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Wed, 9 Dec 2020 20:11:25 -0800 Subject: [PATCH 251/299] 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 26728348f69ec6ee58ccbbe4443af68e3f0cf80f Mon Sep 17 00:00:00 2001 From: Maikel Steneker Date: Wed, 9 Dec 2020 16:08:04 +0100 Subject: [PATCH 252/299] Add support for C++14 binary literals --- pmd-cpp/etc/grammar/cpp.jj | 4 +++- .../pmd/lang/cpp/cpd/testdata/literals.cpp | 3 +++ .../pmd/lang/cpp/cpd/testdata/literals.txt | 14 +++++++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pmd-cpp/etc/grammar/cpp.jj b/pmd-cpp/etc/grammar/cpp.jj index 4c10ad9d66..5790cf4062 100644 --- a/pmd-cpp/etc/grammar/cpp.jj +++ b/pmd-cpp/etc/grammar/cpp.jj @@ -284,12 +284,14 @@ TOKEN : TOKEN: { - < #DECIMALDIGIT: ["0"-"9"] > + < #BINARYDIGIT: ["0"-"1"] > | < #OCTALDIGIT: ["0"-"7"] > +| < #DECIMALDIGIT: ["0"-"9"] > | < #HEXDIGIT: ["a"-"f", "A"-"F", "0"-"9"] > | < #INT_SUFFIX: ["u", "U", "l", "L"] | "uL" | "Ul" | "UL" | "ul" | "lu" | "Lu" | "lU" | "LU" > | < ZERO: "0" > +| < BINARY_INT_LITERAL: "0" ["b", "B"] ("'" | )+ ()? > | < OCTAL_INT_LITERAL: "0" ("'" | )+ ()? > | < DECIMAL_INT_LITERAL: ["1"-"9"] ("'" | )* ()? > | < HEXADECIMAL_INT_LITERAL: "0" ["x", "X"] ("'" | )+ ()? > diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.cpp b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.cpp index 9f0d38b06f..cbae7336ba 100644 --- a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.cpp +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.cpp @@ -37,4 +37,7 @@ auto hex_literal = 0x0F00'abcd'6f3d; auto silly_example = 1'0'0'000'00; + // boolean literals + int b1 = 0B001101; // C++ 14 binary literal + int b2 = 0b000001; // C++ 14 binary literal } \ No newline at end of file diff --git a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.txt b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.txt index 1e792e7302..174e6325c4 100644 --- a/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.txt +++ b/pmd-cpp/src/test/resources/net/sourceforge/pmd/lang/cpp/cpd/testdata/literals.txt @@ -118,6 +118,18 @@ L38 [=] 24 24 [1'0'0'000'00] 26 37 [;] 38 38 -L40 +L41 + [int] 5 7 + [b1] 9 10 + [=] 12 12 + [0B001101] 14 21 + [;] 22 22 +L42 + [int] 5 7 + [b2] 9 10 + [=] 12 12 + [0b000001] 14 21 + [;] 22 22 +L43 [}] 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 253/299] 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 254/299] 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 0b6f3cb991fb1588c3d2549af838aa76e6bd1519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 10 Dec 2020 20:54:20 +0100 Subject: [PATCH 255/299] [java] Remove deprecated rules PositionLiteralFirstInCaseInsensitiveComparsons, PositionLiteralsFirstInComparisons Refs #2493, #2701, #2022 --- docs/pages/7_0_0_release_notes.md | 2 + ...actPositionLiteralsFirstInComparisons.java | 123 ------------------ ...FirstInCaseInsensitiveComparisonsRule.java | 17 --- ...ositionLiteralsFirstInComparisonsRule.java | 17 --- .../resources/category/java/bestpractices.xml | 50 ------- ...FirstInCaseInsensitiveComparisonsTest.java | 12 -- ...ositionLiteralsFirstInComparisonsTest.java | 12 -- ...eralsFirstInCaseInsensitiveComparisons.xml | 77 ----------- .../PositionLiteralsFirstInComparisons.xml | 99 -------------- 9 files changed, 2 insertions(+), 407 deletions(-) delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractPositionLiteralsFirstInComparisons.java delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsRule.java delete mode 100644 pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsRule.java delete mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsTest.java delete mode 100644 pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsTest.java delete mode 100644 pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PositionLiteralsFirstInCaseInsensitiveComparisons.xml delete mode 100644 pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PositionLiteralsFirstInComparisons.xml diff --git a/docs/pages/7_0_0_release_notes.md b/docs/pages/7_0_0_release_notes.md index 4acb29aca0..03c8619c6a 100644 --- a/docs/pages/7_0_0_release_notes.md +++ b/docs/pages/7_0_0_release_notes.md @@ -98,6 +98,8 @@ The following previously deprecated rules have been finally removed: * LoggerIsNotStaticFinal (java-errorprone) * MIsLeadingVariableName (java-codestyle) * ModifiedCyclomaticComplexity (java-design) +* PositionLiteralsFirstInCaseInsensitiveComparisons (java-bestpractices) +* PositionLiteralsFirstInComparisons (java-bestpractices) * StdCyclomaticComplexity (java-design) * SuspiciousConstantFieldName (java-codestyle) * UnsynchronizedStaticDateFormatter (java-multithreading) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractPositionLiteralsFirstInComparisons.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractPositionLiteralsFirstInComparisons.java deleted file mode 100644 index 9c924643c3..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/AbstractPositionLiteralsFirstInComparisons.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.bestpractices; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; -import net.sourceforge.pmd.lang.java.ast.ASTArguments; -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.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.rule.AbstractJavaRule; - -/** - * @deprecated Replaced by {@link LiteralsFirstInComparisonsRule} - */ -@Deprecated -class AbstractPositionLiteralsFirstInComparisons extends AbstractJavaRule { - - private final String equalsImage; - - AbstractPositionLiteralsFirstInComparisons(String equalsImage) { - addRuleChainVisit(ASTPrimaryExpression.class); - this.equalsImage = equalsImage; - } - - @Override - public Object visit(ASTPrimaryExpression node, Object data) { - ASTPrimaryPrefix primaryPrefix = node.getFirstChildOfType(ASTPrimaryPrefix.class); - ASTPrimarySuffix primarySuffix = node.getFirstChildOfType(ASTPrimarySuffix.class); - if (primaryPrefix != null && primarySuffix != null) { - ASTName name = primaryPrefix.getFirstChildOfType(ASTName.class); - if (name == null || !name.getImage().endsWith(equalsImage)) { - return data; - } - if (!isSingleStringLiteralArgument(primarySuffix)) { - return data; - } - if (isWithinNullComparison(node)) { - return data; - } - addViolation(data, node); - } - return node; - } - - private boolean isWithinNullComparison(ASTPrimaryExpression node) { - for (ASTExpression parentExpr : node.getParentsOfType(ASTExpression.class)) { - if (isComparisonWithNull(parentExpr, "==", ASTConditionalOrExpression.class) - || isComparisonWithNull(parentExpr, "!=", ASTConditionalAndExpression.class)) { - return true; - } - } - return false; - } - - /* - * Expression/ConditionalAndExpression//EqualityExpression(@Image='!=']//NullLiteral - * Expression/ConditionalOrExpression//EqualityExpression(@Image='==']//NullLiteral - */ - private boolean isComparisonWithNull(ASTExpression parentExpr, String equalOperator, Class condition) { - Node condExpr = null; - ASTEqualityExpression eqExpr = null; - if (parentExpr != null) { - condExpr = parentExpr.getFirstChildOfType(condition); - } - if (condExpr != null) { - eqExpr = condExpr.getFirstDescendantOfType(ASTEqualityExpression.class); - } - if (eqExpr != null) { - return eqExpr.hasImageEqualTo(equalOperator) && eqExpr.hasDescendantOfType(ASTNullLiteral.class); - } - return false; - } - - /* - * This corresponds to the following XPath expression: - * (../PrimarySuffix/Arguments/ArgumentList/Expression/PrimaryExpression/PrimaryPrefix/Literal[@StringLiteral= true()]) - * and - * ( count(../PrimarySuffix/Arguments/ArgumentList/Expression) = 1 ) - */ - private boolean isSingleStringLiteralArgument(ASTPrimarySuffix primarySuffix) { - if (!primarySuffix.isArguments() || primarySuffix.getArgumentCount() != 1) { - return false; - } - Node node = primarySuffix; - node = node.getFirstChildOfType(ASTArguments.class); - if (node != null) { - node = node.getFirstChildOfType(ASTArgumentList.class); - if (node.getNumChildren() != 1) { - return false; - } - } - if (node != null) { - node = node.getFirstChildOfType(ASTExpression.class); - } - if (node != null) { - node = node.getFirstChildOfType(ASTPrimaryExpression.class); - } - if (node != null) { - node = node.getFirstChildOfType(ASTPrimaryPrefix.class); - } - if (node != null) { - node = node.getFirstChildOfType(ASTLiteral.class); - } - if (node != null) { - ASTLiteral literal = (ASTLiteral) node; - if (literal.isStringLiteral()) { - return true; - } - } - return false; - } -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsRule.java deleted file mode 100644 index 9c35fb226b..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisonsRule.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.bestpractices; - -/** - * @deprecated Replaced by {@link LiteralsFirstInComparisonsRule} - */ -@Deprecated -public class PositionLiteralsFirstInCaseInsensitiveComparisonsRule extends AbstractPositionLiteralsFirstInComparisons { - - public PositionLiteralsFirstInCaseInsensitiveComparisonsRule() { - super(".equalsIgnoreCase"); - } - -} diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsRule.java deleted file mode 100644 index 7ce54ca3df..0000000000 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/PositionLiteralsFirstInComparisonsRule.java +++ /dev/null @@ -1,17 +0,0 @@ -/* - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.java.rule.bestpractices; - -/** - * @deprecated Replaced by {@link LiteralsFirstInComparisonsRule} - */ -@Deprecated -public class PositionLiteralsFirstInComparisonsRule extends AbstractPositionLiteralsFirstInComparisons { - - public PositionLiteralsFirstInComparisonsRule() { - super(".equals"); - } - -} diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 6b64f1c08d..767431d2fc 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -1043,56 +1043,6 @@ String name, - - -Position literals first in comparisons, if the second argument is null then NullPointerExceptions -can be avoided, they will just return false. - -This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons" %}. - - 3 - - - - - - - -Position literals first in comparisons, if the second argument is null then NullPointerExceptions -can be avoided, they will just return false. - -This rule is replaced by the more general rule {% rule "LiteralsFirstInComparisons" %}. - - 3 - - - - - - - - - ok, literal comes first - 0 - - - - - bad, literal comes last - 1 - - - - - ok - 0 - - - - - Test case from bug [1472195] - PositionLiteralsFirstInComparisons give many false positives - 0 - - - - - Test case from bug [1472195] - PositionLiteralsFirstInComparisons give many false positives - 0 - - - diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PositionLiteralsFirstInComparisons.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PositionLiteralsFirstInComparisons.xml deleted file mode 100644 index 7fd74de81e..0000000000 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/PositionLiteralsFirstInComparisons.xml +++ /dev/null @@ -1,99 +0,0 @@ - - - - - ok, literal comes first - 0 - - - - - bad, literal comes last - 1 - - - - - ok - 0 - - - - - Test case from bug [1472195] - PositionLiteralsFirstInComparisons give many false positives - 0 - - - - - Test case from bug [1472195] - PositionLiteralsFirstInComparisons give many false positives - 0 - - - - - #1256 PositionLiteralsFirstInComparisons false positive with Characters - 0 - - - From 8743c0a256a5a89b4f78746394f14049c6d6ee30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 10 Dec 2020 23:49:27 +0100 Subject: [PATCH 256/299] Add some tests --- .../net/sourceforge/pmd/lang/java/types/Lub.java | 11 ++++++++--- .../net/sourceforge/pmd/lang/java/types/GlbTest.kt | 13 ++++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java index eb74945500..4f6ef77b77 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java @@ -345,9 +345,6 @@ final class Lub { JTypeMirror lastBadClass = null; for (int i = 0; i < bounds.size(); i++) { JTypeMirror ci = bounds.get(i); - if (ci.isPrimitive() || ci instanceof JWildcardType || ci instanceof JIntersectionType) { - throw new IllegalArgumentException("Bad intersection type component: " + ci + " in " + types); - } if (isExclusiveIntersectionBound(ci)) { // either Ci is an array, or Ci is a class, or Ci is a type var (possibly captured) @@ -395,6 +392,12 @@ final class Lub { return new JIntersectionType(ts, ck, bounds); } + private static void checkGlbComponent(Collection types, JTypeMirror ci) { + if (ci.isPrimitive() || ci instanceof JWildcardType || ci instanceof JIntersectionType) { + throw new IllegalArgumentException("Bad intersection type component: " + ci + " in " + types); + } + } + private static @NonNull List flattenRemoveTrivialBound(Collection types) { ArrayList bounds = new ArrayList<>(types.size()); @@ -403,6 +406,8 @@ final class Lub { if (type instanceof JIntersectionType) { bounds.addAll(((JIntersectionType) type).getComponents()); } else { + checkGlbComponent(types, type); + if (!type.isTop()) { bounds.add(type); } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt index db7fae2567..ddc3c51b35 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.java.types +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder @@ -36,7 +37,7 @@ class GlbTest : FunSpec({ } // in particular - checkAll(ts.allTypesGen) { t -> + checkAll(ts.refTypeGen) { t -> glb(t, t) shouldBe t // regardless of what kind of type t is } } @@ -76,6 +77,16 @@ class GlbTest : FunSpec({ } + test("Test GLB errors") { + + shouldThrow { + glb(int, t_Number) + } + shouldThrow { + glb(int, ts.OBJECT) + } + } + test("Test GLB corner cases") { glb(t_Iterable[`?` extends t_Number], t_Iterable[t_String]).shouldBeA { From 952a96735ce6ab0bd80509ff26cf09e49de67b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Thu, 10 Dec 2020 23:49:27 +0100 Subject: [PATCH 257/299] Add tests for GLB --- .../net/sourceforge/pmd/lang/java/types/Lub.java | 11 ++++++++--- .../net/sourceforge/pmd/lang/java/types/GlbTest.kt | 13 ++++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java index eb74945500..4f6ef77b77 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java @@ -345,9 +345,6 @@ final class Lub { JTypeMirror lastBadClass = null; for (int i = 0; i < bounds.size(); i++) { JTypeMirror ci = bounds.get(i); - if (ci.isPrimitive() || ci instanceof JWildcardType || ci instanceof JIntersectionType) { - throw new IllegalArgumentException("Bad intersection type component: " + ci + " in " + types); - } if (isExclusiveIntersectionBound(ci)) { // either Ci is an array, or Ci is a class, or Ci is a type var (possibly captured) @@ -395,6 +392,12 @@ final class Lub { return new JIntersectionType(ts, ck, bounds); } + private static void checkGlbComponent(Collection types, JTypeMirror ci) { + if (ci.isPrimitive() || ci instanceof JWildcardType || ci instanceof JIntersectionType) { + throw new IllegalArgumentException("Bad intersection type component: " + ci + " in " + types); + } + } + private static @NonNull List flattenRemoveTrivialBound(Collection types) { ArrayList bounds = new ArrayList<>(types.size()); @@ -403,6 +406,8 @@ final class Lub { if (type instanceof JIntersectionType) { bounds.addAll(((JIntersectionType) type).getComponents()); } else { + checkGlbComponent(types, type); + if (!type.isTop()) { bounds.add(type); } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt index db7fae2567..ddc3c51b35 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.java.types +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.collections.shouldContainExactly import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder @@ -36,7 +37,7 @@ class GlbTest : FunSpec({ } // in particular - checkAll(ts.allTypesGen) { t -> + checkAll(ts.refTypeGen) { t -> glb(t, t) shouldBe t // regardless of what kind of type t is } } @@ -76,6 +77,16 @@ class GlbTest : FunSpec({ } + test("Test GLB errors") { + + shouldThrow { + glb(int, t_Number) + } + shouldThrow { + glb(int, ts.OBJECT) + } + } + test("Test GLB corner cases") { glb(t_Iterable[`?` extends t_Number], t_Iterable[t_String]).shouldBeA { From 4e00c204991b8a3bbff9012b1d6f264e14f8166e Mon Sep 17 00:00:00 2001 From: Jeff Bartolotta Date: Thu, 10 Dec 2020 22:20:10 -0800 Subject: [PATCH 258/299] 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 a77908c16a73690707a3c1bb63cac49a1ce3b001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 11 Dec 2020 06:25:26 +0100 Subject: [PATCH 259/299] Remove some dead code in GLB This was needed at some point, but now dead, can most likely be removed. --- .../sourceforge/pmd/lang/java/types/Lub.java | 65 ++++--------------- .../pmd/lang/java/types/TypeOps.java | 6 +- 2 files changed, 15 insertions(+), 56 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java index 4f6ef77b77..203e605a75 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java @@ -5,9 +5,6 @@ package net.sourceforge.pmd.lang.java.types; import static net.sourceforge.pmd.lang.java.types.TypeOps.typeArgContains; -import static net.sourceforge.pmd.util.OptionalBool.NO; -import static net.sourceforge.pmd.util.OptionalBool.UNKNOWN; -import static net.sourceforge.pmd.util.OptionalBool.YES; import java.util.ArrayList; import java.util.Arrays; @@ -22,14 +19,9 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.pcollections.ConsPStack; -import org.pcollections.HashTreePSet; -import org.pcollections.PSet; -import org.pcollections.PStack; import net.sourceforge.pmd.lang.java.types.internal.infer.InferenceVar; import net.sourceforge.pmd.util.CollectionUtil; -import net.sourceforge.pmd.util.OptionalBool; /** * Helper class for {@link TypeSystem#lub(Collection)} and {@link TypeSystem#glb(Collection)}. @@ -336,60 +328,34 @@ final class Lub { List bounds = new ArrayList<>(mostSpecific); - JTypeMirror ck = null; // Ck is the primary bound - int primaryIdx = 0; + JTypeMirror primaryBound = null; - OptionalBool retryWithCaptureBounds = NO; - PSet cvarLowers = HashTreePSet.empty(); - PStack cvarsToRemove = ConsPStack.empty(); - JTypeMirror lastBadClass = null; for (int i = 0; i < bounds.size(); i++) { JTypeMirror ci = bounds.get(i); if (isExclusiveIntersectionBound(ci)) { // either Ci is an array, or Ci is a class, or Ci is a type var (possibly captured) // Ci is not unresolved - if (ck == null) { - ck = ci; - primaryIdx = i; + if (primaryBound == null) { + primaryBound = ci; + // move primary bound first + Collections.swap(bounds, 0, i); } else { - JTypeMirror lower = cvarLowerBound(ci); - if (lower != ci && lower != ts.NULL_TYPE) { // NOPMD CompareObjectsWithEquals - cvarLowers = cvarLowers.plus(lower); - cvarsToRemove = cvarsToRemove.plus(ci); - retryWithCaptureBounds = YES; - } else { - retryWithCaptureBounds = retryWithCaptureBounds == YES ? YES - : UNKNOWN; - } - lastBadClass = ci; + throw new IllegalArgumentException( + "Bad intersection, unrelated class types " + ci + " and " + primaryBound + " in " + types + ); } } } - switch (retryWithCaptureBounds) { // when several capture variables were found - case YES: - bounds.removeAll(cvarsToRemove); - bounds.addAll(cvarLowers); - return glb(ts, bounds); - case NO: - break; - default: - throw new IllegalArgumentException( - "Bad intersection, unrelated class types " + lastBadClass + " and " + ck + " in " + types); - } - - if (ck == null) { + if (primaryBound == null) { if (bounds.size() == 1) { return bounds.get(0); } - ck = ts.OBJECT; - } else { - // move primary bound first - Collections.swap(bounds, 0, primaryIdx); + primaryBound = ts.OBJECT; } - return new JIntersectionType(ts, ck, bounds); + return new JIntersectionType(ts, primaryBound, bounds); } private static void checkGlbComponent(Collection types, JTypeMirror ci) { @@ -399,7 +365,7 @@ final class Lub { } private static @NonNull List flattenRemoveTrivialBound(Collection types) { - ArrayList bounds = new ArrayList<>(types.size()); + List bounds = new ArrayList<>(types.size()); for (JTypeMirror type : types) { // flatten intersections: (A & (B & C)) => (A & B & C) @@ -423,12 +389,5 @@ final class Lub { && (ci.getSymbol() == null || !ci.getSymbol().isUnresolved()); } - private static JTypeMirror cvarLowerBound(JTypeMirror t) { - if (t instanceof JTypeVar && ((JTypeVar) t).isCaptured()) { - return ((JTypeVar) t).getLowerBound(); - } - return t; - } - } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java index 9a9b709e89..3bceea2880 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java @@ -434,7 +434,7 @@ public final class TypeOps { * implies convertibility (the conversion is technically called * "widening reference conversion"). You can check those cases using: * - * {@link #bySubtyping() t.isConvertibleTo(s).naturally()} + * {@link #bySubtyping() t.isConvertibleTo(s).bySubtyping()} * *

Unchecked conversion may go backwards from subtyping. For example, * {@code List} is a subtype of the raw type {@code List}, and @@ -475,8 +475,8 @@ public final class TypeOps { * {@code T <: |S|} and {@code T For example, {@code List} is a subtype of the raw * {@code Collection}, not a subtype of {@code Collection}, From ce85132d5b4ad9826c52fedd8003623851a4c63a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 11 Dec 2020 06:48:15 +0100 Subject: [PATCH 260/299] Cleanup captured type var --- .../sourceforge/pmd/lang/java/types/TypeVarImpl.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeVarImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeVarImpl.java index 979352d446..3eb68145b9 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeVarImpl.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeVarImpl.java @@ -143,8 +143,7 @@ abstract class TypeVarImpl implements JTypeVar { private static final int PRIME = 997; // largest prime less than 1000 - private final JWildcardType wildcard; - private final int captureId = hashCode() % PRIME; + private final @NonNull JWildcardType wildcard; private JTypeMirror upperBound; private JTypeMirror lowerBound; @@ -160,10 +159,6 @@ abstract class TypeVarImpl implements JTypeVar { this.wildcard = wild; } - public JWildcardType getWildcard() { - return wildcard; - } - void setUpperBound(@NonNull JTypeMirror upperBound) { this.upperBound = upperBound; } @@ -195,7 +190,7 @@ abstract class TypeVarImpl implements JTypeVar { } @Override - public @Nullable JWildcardType getCapturedOrigin() { + public JWildcardType getCapturedOrigin() { return wildcard; } @@ -232,7 +227,7 @@ abstract class TypeVarImpl implements JTypeVar { @Override public @NonNull String getName() { - return "capture#" + captureId + " of " + wildcard; + return "capture#" + hashCode() % PRIME + " of " + wildcard; } } } From 593fa9010c8e789d4ccba78f94d1dc1fde6a728d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 11 Dec 2020 07:33:54 +0100 Subject: [PATCH 261/299] Add some tests for lub --- .../sourceforge/pmd/lang/java/types/Lub.java | 12 ++----- .../pmd/lang/java/types/GlbTest.kt | 14 +++++---- .../pmd/lang/java/types/LubTest.kt | 31 +++++++++++++++---- 3 files changed, 35 insertions(+), 22 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java index 203e605a75..154db01020 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.lang.java.types; -import static net.sourceforge.pmd.lang.java.types.TypeOps.typeArgContains; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -62,10 +60,10 @@ final class Lub { /** * The "relevant" parameterizations of G, Relevant(G), is: * - *

+     * 
{@code
      * Relevant(G) = { V | 1 ≤ i ≤ k: V in ST(Ui) and V = G<...> }
      *             = { V ∈ stunion | V = G<...> }
-     * 
+ * }
* *

G must be erased (raw). * @@ -213,12 +211,6 @@ final class Lub { */ private JTypeMirror lcta(JTypeMirror t, JTypeMirror s) { - if (typeArgContains(t, s).somehow()) { - return t; - } else if (typeArgContains(s, t).somehow()) { - return s; - } - TypePair pair = new TypePair(t, s); if (lubCache.add(pair)) { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt index ddc3c51b35..5f8b134f08 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/GlbTest.kt @@ -7,17 +7,11 @@ package net.sourceforge.pmd.lang.java.types import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.collections.shouldContainExactly -import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.shouldBe -import io.kotest.property.PropTestConfig import io.kotest.property.checkAll import io.kotest.property.forAll -import io.mockk.InternalPlatformDsl.toArray import net.sourceforge.pmd.lang.ast.test.shouldBeA -import net.sourceforge.pmd.lang.java.types.testdata.LubTestData -import net.sourceforge.pmd.lang.java.types.testdata.LubTestData.* -import java.io.Serializable /** * Tests "the greatest lower bound" (glb). @@ -77,6 +71,14 @@ class GlbTest : FunSpec({ } + + test("Test lub of zero types") { + shouldThrow { + ts.glb(emptyList()) + } + } + + test("Test GLB errors") { shouldThrow { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/LubTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/LubTest.kt index af1c0a62fe..c863a5079c 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/LubTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/LubTest.kt @@ -4,14 +4,11 @@ package net.sourceforge.pmd.lang.java.types +import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.FunSpec -import io.kotest.matchers.collections.shouldContainExactly -import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.shouldBe import io.kotest.property.checkAll -import net.sourceforge.pmd.lang.java.types.testdata.LubTestData import net.sourceforge.pmd.lang.java.types.testdata.LubTestData.* -import java.io.Serializable /** * Tests "least upper bound" (lub). @@ -83,10 +80,26 @@ class LubTest : FunSpec({ ) shouldBe listOf(`t_List{Integer}`, `t_List{String}`) } - test("Test lub with related type arguments") { + test("Test lub with related type arguments (LCTA)") { + + fun checkLcta(vararg components: JTypeMirror, expected: JTypeMirror) { + val l = ts.lub(components.map { GenericSub::class[it] }) as JClassType + l.typeArgs[0] shouldBe expected + } - lub(GenericSub::class[t_Integer], GenericSub::class[t_Number]) shouldBe GenericSub::class[`?` extends t_Number] lub(GenericSub::class[t_Integer], GenericSub::class[`?` extends t_Number]) shouldBe GenericSub::class[`?` extends t_Number] + + checkLcta(t_Integer, t_Number, expected = `?` extends t_Number) + checkLcta(t_Integer, `?` extends t_Number, expected = `?` extends t_Number) + checkLcta(t_Integer, `?` `super` t_Number, expected = `?` `super` t_Integer) + checkLcta(t_Integer, `?` `super` t_Integer, expected = `?` `super` t_Integer) + checkLcta(t_Integer, `?` extends t_Integer, expected = `?` extends t_Integer) + + checkLcta(`?` `super` t_Integer, `?` `super` t_Number, expected = `?` `super` t_Integer) + checkLcta(`?` extends t_Integer, `?` extends t_Number, expected = `?` extends t_Number) + checkLcta(`?` extends t_Integer, `?` `super` t_Number, expected = `?`) + checkLcta(`?` `super` t_Integer, `?` extends t_Number, expected = `?`) + } test("Test lub with identical type arguments") { @@ -109,6 +122,12 @@ class LubTest : FunSpec({ } + test("Test lub of zero types") { + shouldThrow { + ts.lub(emptyList()) + } + } + test("Test lub with interface intersection") { // this example recurses into lub(Comparable, Comparable), at which point From 524eb85cd3223fed882cde6aab16da8ccc26428a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 11 Dec 2020 07:47:16 +0100 Subject: [PATCH 262/299] Make MapFunction abstract the apply implementation is dead code --- .../pmd/lang/java/types/MapFunction.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/MapFunction.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/MapFunction.java index 79b78eb95f..cd85884be7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/MapFunction.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/MapFunction.java @@ -9,14 +9,11 @@ import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; - /** - * A partial function built on a map + * A partial function built on a map. */ -class MapFunction implements Function { +abstract class MapFunction implements Function { private final Map map; @@ -24,7 +21,7 @@ class MapFunction implements Function { this.map = map; } - public Map getMap() { + protected Map getMap() { return map; } @@ -32,11 +29,6 @@ class MapFunction implements Function { return map.isEmpty(); } - @Override - public @Nullable R apply(@NonNull T var) { - return map.get(var); - } - @Override public String toString() { return map.entrySet().stream() From a8d40342f7febf5f8ca85922db8d2360d8de5b1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 11 Dec 2020 10:30:37 +0100 Subject: [PATCH 263/299] Fix compil, remove dependency on xpath --- .../sourceforge/pmd/lang/ParserOptions.java | 2 +- pmd-dist/pom.xml | 5 ++ .../pmd/lang/ParserOptionsTestUtils.java | 16 +++--- .../pmd/lang/ParserOptionsUnitTest.java | 3 +- .../pmd/lang/vf/ast/AbstractVFDataNode.java | 7 +-- .../sourceforge/pmd/lang/vf/VFTestUtils.java | 48 +--------------- .../pmd/lang/vf/ast/ASTExpressionTest.java | 23 ++++---- ...ctVfNodesTest.java => AbstractVfTest.java} | 2 +- .../vf/ast/ApexClassPropertyTypesTest.java | 4 +- .../ApexClassPropertyTypesVisitorTest.java | 1 - .../pmd/lang/vf/ast/ObjectFieldTypesTest.java | 19 ++++++- .../pmd/lang/vf/ast/VfDocStyleTest.java | 2 +- .../vf/ast/VfExpressionTypeVisitorTest.java | 57 +++++++++++-------- .../pmd/lang/vf/ast/VfPageStyleTest.java | 2 +- .../pmd/lang/vf/ast/VfParserTest.java | 2 +- .../metadata/sfdx/classes/ApexController.cls | 0 .../metadata/sfdx/pages/SomePage.page | 0 .../metadata/sfdx/classes/ApexController.cls | 0 .../metadata/mdapi/objects/Account.object | 0 .../metadata/mdapi/pages/SomePage.page | 0 .../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 | 0 .../metadata/sfdx/classes/ApexController.cls | 0 .../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/ApexController.page | 0 .../metadata/sfdx/pages/StandardAccount.page | 0 36 files changed, 88 insertions(+), 105 deletions(-) rename pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/{AbstractVfNodesTest.java => AbstractVfTest.java} (84%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.cls (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/ApexClassPropertyTypesVisitor/metadata/sfdx/classes/ApexController.cls (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/ObjectFieldTypes/metadata/mdapi/objects/Account.object (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/VfExpressionTypeVisitor/metadata/sfdx/classes/ApexController.cls (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/VfExpressionTypeVisitor/metadata/sfdx/pages/ApexController.page (100%) rename pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/{ => ast}/VfExpressionTypeVisitor/metadata/sfdx/pages/StandardAccount.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 5074416b49..d5e41e7e45 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 @@ -81,7 +81,7 @@ public class ParserOptions { @Override public int hashCode() { - return getSuppressMarker().hashCode(); + return Objects.hash(suppressMarker, languageId, parserOptionsProperties); } /** diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index eef2e85125..1663f9d031 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -213,6 +213,11 @@ pmd-ui ${pmd-designer.version} + + net.sourceforge.pmd + pmd-visualforce + ${project.version} + net.sourceforge.pmd pmd-vm diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTestUtils.java b/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTestUtils.java index cab491c5e8..d8612c4051 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTestUtils.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTestUtils.java @@ -35,23 +35,23 @@ public final class ParserOptionsTestUtils { // Check all 16 equality combinations Assert.assertEquals(options1, options1); - Assert.assertFalse(options1.equals(options2)); + Assert.assertNotEquals(options1, options2); Assert.assertEquals(options1, options3); - Assert.assertFalse(options1.equals(options4)); + Assert.assertNotEquals(options1, options4); - Assert.assertFalse(options2.equals(options1)); + Assert.assertNotEquals(options2, options1); Assert.assertEquals(options2, options2); - Assert.assertFalse(options2.equals(options3)); + Assert.assertNotEquals(options2, options3); Assert.assertEquals(options2, options4); Assert.assertEquals(options3, options1); - Assert.assertFalse(options3.equals(options2)); + Assert.assertNotEquals(options3, options2); Assert.assertEquals(options3, options3); - Assert.assertFalse(options3.equals(options4)); + Assert.assertNotEquals(options3, options4); - Assert.assertFalse(options4.equals(options1)); + Assert.assertNotEquals(options4, options1); Assert.assertEquals(options4, options2); - Assert.assertFalse(options4.equals(options3)); + Assert.assertNotEquals(options4, options3); Assert.assertEquals(options4, options4); // Hashcodes should match up 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 0fc7a8aa10..8fc99fe8d5 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 @@ -15,6 +15,7 @@ import org.apache.commons.lang3.StringUtils; import org.junit.Assert; import org.junit.Test; +import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; import net.sourceforge.pmd.test.lang.DummyLanguageModule; @@ -60,7 +61,7 @@ public class ParserOptionsUnitTest { @Test public void testSuppressMarker() { ParserOptions parserOptions = new ParserOptions(); - Assert.assertNull(parserOptions.getSuppressMarker()); + Assert.assertEquals(PMD.SUPPRESS_MARKER, parserOptions.getSuppressMarker()); parserOptions.setSuppressMarker("foo"); Assert.assertEquals("foo", parserOptions.getSuppressMarker()); } 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 88c65928b6..94a7a4b951 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 @@ -9,7 +9,7 @@ import net.sourceforge.pmd.lang.vf.DataType; /** * Represents a node that displays a piece of data. */ -class AbstractVFDataNode extends AbstractVFNode implements VfTypedNode { +abstract class AbstractVFDataNode extends AbstractVfNode implements VfTypedNode { private DataType dataType; @@ -17,11 +17,6 @@ class AbstractVFDataNode extends AbstractVFNode implements VfTypedNode { super(id); } - AbstractVFDataNode(VfParser parser, int id) { - super(id); - this.parser = parser; - } - @Override public DataType getDataType() { return dataType; 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 07922382cf..ea6980dcb2 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,22 +4,13 @@ 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.lang.vf.ast.SalesforceFieldTypes; -import net.sourceforge.pmd.properties.PropertyDescriptor; public final class VFTestUtils { + private 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. @@ -72,37 +63,4 @@ 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/ast/ASTExpressionTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ASTExpressionTest.java index 2f3975008f..803b1e83c5 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 @@ -16,7 +16,6 @@ import java.util.stream.Collectors; import org.junit.Test; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.vf.VFTestUtils; import net.sourceforge.pmd.util.treeexport.XmlTreeRenderer; public class ASTExpressionTest { @@ -34,7 +33,7 @@ public class ASTExpressionTest { for (String template : SNIPPET_TEMPLATES) { ASTCompilationUnit compilationUnit = compile(String.format(template, "MyValue")); - List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + List nodes = getExpressions(compilationUnit); assertEquals(template, 1, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); @@ -52,7 +51,7 @@ public class ASTExpressionTest { for (String template : SNIPPET_TEMPLATES) { ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject__c.Text__c")); - List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + List nodes = getExpressions(compilationUnit); assertEquals(template, 1, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); @@ -70,7 +69,7 @@ public class ASTExpressionTest { for (String template : SNIPPET_TEMPLATES) { ASTCompilationUnit compilationUnit = compile(String.format(template, "userOptions.0")); - List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + List nodes = getExpressions(compilationUnit); assertEquals(template, 1, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); @@ -88,7 +87,7 @@ public class ASTExpressionTest { 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"); + List nodes = getExpressions(compilationUnit); assertEquals(template, 1, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); @@ -109,7 +108,7 @@ public class ASTExpressionTest { for (String template : SNIPPET_TEMPLATES) { ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject1__c.MyObject2__r.Text__c")); - List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + List nodes = getExpressions(compilationUnit); assertEquals(template, 1, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); @@ -128,7 +127,7 @@ public class ASTExpressionTest { 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"); + List nodes = getExpressions(compilationUnit); assertEquals(template, 1, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); @@ -153,7 +152,7 @@ public class ASTExpressionTest { for (String template : SNIPPET_TEMPLATES) { ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject__c['Name']")); - List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + List nodes = getExpressions(compilationUnit); assertEquals(template, 2, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); @@ -171,7 +170,7 @@ public class ASTExpressionTest { for (String template : SNIPPET_TEMPLATES) { ASTCompilationUnit compilationUnit = compile(String.format(template, "MyObject1__c['MyObject2__r'].Text__c")); - List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + List nodes = getExpressions(compilationUnit); assertEquals(template, 2, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); @@ -189,7 +188,7 @@ public class ASTExpressionTest { for (String template : SNIPPET_TEMPLATES) { ASTCompilationUnit compilationUnit = compile(String.format(template, "theLineItems[item.Id].UnitPrice")); - List nodes = VFTestUtils.findNodes(compilationUnit, "//Expression"); + List nodes = getExpressions(compilationUnit); assertEquals(template, 2, nodes.size()); ASTExpression expression = (ASTExpression) nodes.get(0); @@ -202,6 +201,10 @@ public class ASTExpressionTest { } } + private static List getExpressions(ASTCompilationUnit compilationUnit) { + return compilationUnit.descendants(ASTExpression.class).toList(it -> it); + } + /** * Invert the map to make it easier to unit test. */ diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfNodesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfTest.java similarity index 84% rename from pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfNodesTest.java rename to pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfTest.java index d905a3817a..ab922a2abf 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfNodesTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/AbstractVfTest.java @@ -4,7 +4,7 @@ package net.sourceforge.pmd.lang.vf.ast; -public abstract class AbstractVfNodesTest { +public abstract class AbstractVfTest { protected final VfParsingHelper vf = VfParsingHelper.DEFAULT.withResourceContext(getClass()); diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesTest.java index 4ba9d16f1f..d8e14a70b8 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesTest.java @@ -57,8 +57,8 @@ public class ApexClassPropertyTypesTest { .resolve("SomePage.page"); ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); - VFTestUtils.validateDataTypes(EXPECTED_DATA_TYPES, apexClassPropertyTypes, vfPagePath, - VfParserOptions.APEX_DIRECTORIES_DESCRIPTOR.defaultValue()); + ObjectFieldTypesTest.validateDataTypes(EXPECTED_DATA_TYPES, apexClassPropertyTypes, vfPagePath, + VfParserOptions.APEX_DIRECTORIES_DESCRIPTOR.defaultValue()); } @Test diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitorTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitorTest.java index bb397e7854..4ec20b3dc8 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitorTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitorTest.java @@ -28,7 +28,6 @@ import net.sourceforge.pmd.lang.apex.ApexLanguageModule; import net.sourceforge.pmd.lang.apex.ast.ApexNode; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.vf.VFTestUtils; -import net.sourceforge.pmd.lang.vf.ast.ApexClassPropertyTypesVisitor; import apex.jorje.semantic.symbol.type.BasicType; diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypesTest.java index 322982f7c9..899b2f7012 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypesTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypesTest.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.vf.ast; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import java.nio.file.Path; @@ -19,7 +20,6 @@ import org.junit.Test; import net.sourceforge.pmd.lang.vf.DataType; import net.sourceforge.pmd.lang.vf.VFTestUtils; import net.sourceforge.pmd.lang.vf.VfParserOptions; -import net.sourceforge.pmd.lang.vf.ast.ObjectFieldTypes; public class ObjectFieldTypesTest { private static final Map EXPECTED_SFDX_DATA_TYPES; @@ -106,13 +106,26 @@ public class ObjectFieldTypesTest { * Validate the expected results when the Account Fields are stored in decomposed sfdx format */ private void validateSfdxAccount(ObjectFieldTypes objectFieldTypes, Path vfPagePath, List paths) { - VFTestUtils.validateDataTypes(EXPECTED_SFDX_DATA_TYPES, objectFieldTypes, vfPagePath, paths); + 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) { - VFTestUtils.validateDataTypes(EXPECTED_MDAPI_DATA_TYPES, objectFieldTypes, vfPagePath, paths); + validateDataTypes(EXPECTED_MDAPI_DATA_TYPES, objectFieldTypes, vfPagePath, paths); + } + + /** + * 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)); + } } } diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfDocStyleTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfDocStyleTest.java index 4fe6959f8f..7b0677d1d7 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfDocStyleTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfDocStyleTest.java @@ -24,7 +24,7 @@ import org.junit.Test; * @author sergey.gorbaty - VF adaptation * */ -public class VfDocStyleTest extends AbstractVfNodesTest { +public class VfDocStyleTest extends AbstractVfTest { /** * Smoke test for VF parser. diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitorTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitorTest.java index 3027047a86..9a24cfeaf9 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitorTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitorTest.java @@ -16,6 +16,7 @@ import java.nio.file.Path; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import org.junit.Test; @@ -24,6 +25,7 @@ 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.ast.NodeStream; import net.sourceforge.pmd.lang.vf.DataType; import net.sourceforge.pmd.lang.vf.VFTestUtils; import net.sourceforge.pmd.lang.vf.VfLanguageModule; @@ -62,8 +64,8 @@ public class VfExpressionTypeVisitorTest { 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); + List nodes = getIdentifiers(rootNode, entry); + // Each string appears twice, it is set on a "value" attribute and inline assertEquals(entry.getKey(), 2, nodes.size()); for (Node node : nodes) { @@ -84,9 +86,12 @@ public class VfExpressionTypeVisitorTest { 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); + List nodes = rootNode.descendants(ASTLiteral.class) + // Literals are surrounded by apostrophes + .filterMatching(ASTLiteral::getImage, "'" + entry.getKey() + "'") + .filterMatching(ASTLiteral::getDataType, null) + .toList(); + // Each string appears twice, it is set on a "value" attribute and inline assertEquals(entry.getKey(), 2, nodes.size()); for (Node node : nodes) { @@ -105,15 +110,18 @@ public class VfExpressionTypeVisitorTest { 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 VfTypedNode); - VfTypedNode dataNode = (VfTypedNode) node; - assertNull(dataNode.getDataType()); - } + checkNodes(rootNode.descendants(ASTIdentifier.class) + .filterMatching(ASTIdentifier::getImage, "NotFoundField__c")); + checkNodes(rootNode.descendants(ASTLiteral.class) + .filterMatching(ASTLiteral::getImage, "'NotFoundField__c'")); + } + + private void checkNodes(NodeStream nodeStream) { + // Each string appears twice, it is set on a "value" attribute and inline + List nodes = nodeStream.toList(); + assertEquals(2, nodes.size()); + for (VfTypedNode node : nodes) { + assertNull(node.getDataType()); } } @@ -125,8 +133,8 @@ public class VfExpressionTypeVisitorTest { 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); + List nodes = getIdentifiers(rootNode, entry); + // Each string appears twice, it is set on a "value" attribute and inline assertEquals(entry.getKey(), 2, nodes.size()); for (Node node : nodes) { @@ -138,6 +146,13 @@ public class VfExpressionTypeVisitorTest { } } + private List getIdentifiers(Node rootNode, Entry entry) { + return rootNode.descendants(ASTIdentifier.class) + .filterMatching(ASTIdentifier::getImage, entry.getKey()) + .filterMatching(ASTIdentifier::getDataType, entry.getValue()) + .toList(); + } + /** * Nodes where the DataType can't be determined should have a null DataType */ @@ -145,15 +160,9 @@ public class VfExpressionTypeVisitorTest { 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 VfTypedNode); - VfTypedNode dataNode = (VfTypedNode) node; - assertNull(dataNode.getDataType()); - } + checkNodes(rootNode.descendants(ASTIdentifier.class) + .filterMatching(ASTIdentifier::getImage, "NotFoundProp")); } private Node compile(String pageName) throws FileNotFoundException { diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfPageStyleTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfPageStyleTest.java index 77287f6e42..e5578adc46 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfPageStyleTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfPageStyleTest.java @@ -10,7 +10,7 @@ import java.util.List; import org.junit.Test; -public class VfPageStyleTest extends AbstractVfNodesTest { +public class VfPageStyleTest extends AbstractVfTest { /** * Test parsing of a EL expression. diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParserTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParserTest.java index 6ce22de09c..76a609c289 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParserTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfParserTest.java @@ -11,7 +11,7 @@ import net.sourceforge.pmd.lang.ast.ParseException; /** * @author sergey.gorbaty */ -public class VfParserTest extends AbstractVfNodesTest { +public class VfParserTest extends AbstractVfTest { @Test public void testSingleDoubleQuoteAndEL() { 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/ast/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.cls similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.cls rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes/metadata/sfdx/classes/ApexController.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/ast/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes/metadata/sfdx/pages/SomePage.page 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/ast/ApexClassPropertyTypesVisitor/metadata/sfdx/classes/ApexController.cls similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ApexClassPropertyTypesVisitor/metadata/sfdx/classes/ApexController.cls rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitor/metadata/sfdx/classes/ApexController.cls diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/objects/Account.object b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/mdapi/objects/Account.object similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/objects/Account.object rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/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/ast/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/mdapi/pages/SomePage.page diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/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/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/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/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/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/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/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/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/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/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml diff --git a/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml b/pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/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/ObjectFieldTypes/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/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/ast/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypes/metadata/sfdx/pages/SomePage.page 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/ast/VfExpressionTypeVisitor/metadata/sfdx/classes/ApexController.cls similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/classes/ApexController.cls rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/classes/ApexController.cls 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/ast/VfExpressionTypeVisitor/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/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Checkbox__c.field-meta.xml 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/ast/VfExpressionTypeVisitor/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/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/DateTime__c.field-meta.xml 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/ast/VfExpressionTypeVisitor/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/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/LongTextArea__c.field-meta.xml 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/ast/VfExpressionTypeVisitor/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/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Picklist__c.field-meta.xml 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/ast/VfExpressionTypeVisitor/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/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/TextArea__c.field-meta.xml 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/ast/VfExpressionTypeVisitor/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/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/objects/Account/fields/Text__c.field-meta.xml 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/ast/VfExpressionTypeVisitor/metadata/sfdx/pages/ApexController.page similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/pages/ApexController.page rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/pages/ApexController.page 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/ast/VfExpressionTypeVisitor/metadata/sfdx/pages/StandardAccount.page similarity index 100% rename from pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/VfExpressionTypeVisitor/metadata/sfdx/pages/StandardAccount.page rename to pmd-visualforce/src/test/resources/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor/metadata/sfdx/pages/StandardAccount.page 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 264/299] 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 265/299] [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 266/299] 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 267/299] 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 5de496980867b8e00bb73166e43e77f68987d582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 11 Dec 2020 16:53:04 +0100 Subject: [PATCH 268/299] Fix removed rule references --- pmd-core/src/main/resources/rulesets/releases/33.xml | 2 +- pmd-core/src/main/resources/rulesets/releases/510.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pmd-core/src/main/resources/rulesets/releases/33.xml b/pmd-core/src/main/resources/rulesets/releases/33.xml index 08d1dcd8bf..895e7820d9 100644 --- a/pmd-core/src/main/resources/rulesets/releases/33.xml +++ b/pmd-core/src/main/resources/rulesets/releases/33.xml @@ -8,7 +8,7 @@ This ruleset contains links to rules that are new in PMD v3.3 - + diff --git a/pmd-core/src/main/resources/rulesets/releases/510.xml b/pmd-core/src/main/resources/rulesets/releases/510.xml index 82e6c2530e..09a1172297 100644 --- a/pmd-core/src/main/resources/rulesets/releases/510.xml +++ b/pmd-core/src/main/resources/rulesets/releases/510.xml @@ -12,7 +12,7 @@ This ruleset contains links to rules that are new in PMD v5.1.0 - + From 10a2b40731a07e475cdeaf6567574c91c08524be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 11 Dec 2020 17:44:01 +0100 Subject: [PATCH 269/299] Fix last references --- pmd-java/src/main/resources/rulesets/java/design.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pmd-java/src/main/resources/rulesets/java/design.xml b/pmd-java/src/main/resources/rulesets/java/design.xml index bef304b2e6..9ac4ae2fda 100644 --- a/pmd-java/src/main/resources/rulesets/java/design.xml +++ b/pmd-java/src/main/resources/rulesets/java/design.xml @@ -62,8 +62,6 @@ are suggested. - - From bcbf588bba76c16e5d26121b16d4d7d0a85c174e Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 11 Dec 2020 18:35:41 +0100 Subject: [PATCH 270/299] 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 271/299] 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 8de0e171fd8e79934c256f74d9c27be31a858b82 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 11 Dec 2020 19:55:03 +0100 Subject: [PATCH 272/299] Remove unused import --- pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java | 1 - 1 file changed, 1 deletion(-) 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 f5631f7aec..36ba54503d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -10,7 +10,6 @@ import java.util.ArrayList; 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; From 5d9e657d6e6c5fdc7f4d608dea38dda6709ee3a4 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 11 Dec 2020 20:06:04 +0100 Subject: [PATCH 273/299] Fix old rule references of deleted rules --- docs/pages/next_major_development.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/next_major_development.md b/docs/pages/next_major_development.md index 31dd1316bd..e414e405ce 100644 --- a/docs/pages/next_major_development.md +++ b/docs/pages/next_major_development.md @@ -1192,8 +1192,8 @@ large projects, with many duplications, it was causing `OutOfMemoryError`s (see will be removed with PMD 7.0.0. The rule is replaced by the more general {% rule "java/multithreading/UnsynchronizedStaticFormatter" %}. -* The two Java rules {% rule "java/bestpractices/PositionLiteralsFirstInComparisons" %} - and {% rule "java/bestpractices/PositionLiteralsFirstInCaseInsensitiveComparisons" %} (ruleset `java-bestpractices`) +* The two Java rules [`PositionLiteralsFirstInComparisons`](https://pmd.github.io/pmd-6.29.0/pmd_rules_java_bestpractices.html#positionliteralsfirstincomparisons) + and [`PositionLiteralsFirstInCaseInsensitiveComparisons`](https://pmd.github.io/pmd-6.29.0/pmd_rules_java_bestpractices.html#positionliteralsfirstincaseinsensitivecomparisons) (ruleset `java-bestpractices`) have been deprecated in favor of the new rule {% rule "java/bestpractices/LiteralsFirstInComparisons" %}. * The Java rule [`AvoidFinalLocalVariable`](https://pmd.github.io/pmd-6.16.0/pmd_rules_java_codestyle.html#avoidfinallocalvariable) (`java-codestyle`) has been deprecated From 39f3026cf141b1f954e529e1a090f8abb5408bc4 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 12 Dec 2020 09:31:08 +0100 Subject: [PATCH 274/299] 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 275/299] [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 276/299] [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 277/299] 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 278/299] [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 279/299] [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 3202136bd9798bbfb10c74245779d285f7c0fad3 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 12 Dec 2020 13:11:52 +0100 Subject: [PATCH 280/299] [ci] Fix maven dependencies workaround for dokka --- .ci/inc/maven-dependencies.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/inc/maven-dependencies.inc b/.ci/inc/maven-dependencies.inc index c99279b395..12d1de380a 100644 --- a/.ci/inc/maven-dependencies.inc +++ b/.ci/inc/maven-dependencies.inc @@ -30,7 +30,7 @@ function maven_dependencies_resolve() { # 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:resolve -pl '!pmd-dist,!pmd-doc,!pmd-scala' ./mvnw dependency:get -DgroupId=org.jetbrains.dokka \ -DartifactId=dokka-maven-plugin \ From 134dbed07e8fb20bc01e3543803ca72a065187da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 17:27:20 +0100 Subject: [PATCH 281/299] Simplify compat filter --- .../net/sourceforge/pmd/PMDConfiguration.java | 6 +- .../net/sourceforge/pmd/RuleSetFactory.java | 10 +- .../pmd/RuleSetFactoryCompatibility.java | 283 ++++++++---------- .../pmd/RuleSetFactoryCompatibilityTest.java | 87 +----- 4 files changed, 147 insertions(+), 239 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java index 694519e344..1c77fec7fc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -53,7 +53,7 @@ import net.sourceforge.pmd.util.ClasspathClassLoader; *

  • A comma separated list of input paths to process for source files. This * may include files, directories, archives (e.g. ZIP files), etc. * {@link #getInputPaths()}
  • - *
  • A flag which controls, whether {@link RuleSetFactoryCompatibility} filter + *
  • A flag which controls, whether {@link RuleSetLoader#enableCompatibility(boolean)} filter * should be used or not: #isRuleSetFactoryCompatibilityEnabled; * * @@ -561,7 +561,7 @@ public class PMDConfiguration extends AbstractConfiguration { * * @return true, if the rule set factory compatibility feature is enabled * - * @see RuleSetFactoryCompatibility + * @see RuleSetLoader#enableCompatibility(boolean) */ public boolean isRuleSetFactoryCompatibilityEnabled() { return ruleSetFactoryCompatibilityEnabled; @@ -572,7 +572,7 @@ public class PMDConfiguration extends AbstractConfiguration { * * @param ruleSetFactoryCompatibilityEnabled {@code true} if the feature should be enabled * - * @see RuleSetFactoryCompatibility + * @see RuleSetLoader#enableCompatibility(boolean) */ public void setRuleSetFactoryCompatibilityEnabled(boolean ruleSetFactoryCompatibilityEnabled) { this.ruleSetFactoryCompatibilityEnabled = ruleSetFactoryCompatibilityEnabled; 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 08f2c1bcf7..aa010c7c60 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -362,12 +362,7 @@ public class RuleSetFactory { "Cannot parse a RuleSet from a non-external reference: <" + ruleSetReferenceId + ">."); } DocumentBuilder builder = createDocumentBuilder(); - InputSource inputSource; - if (compatibilityFilter != null) { - inputSource = new InputSource(compatibilityFilter.filterRuleSetFile(inputStream)); - } else { - inputSource = new InputSource(inputStream); - } + InputSource inputSource = new InputSource(inputStream); Document document = builder.parse(inputSource); Element ruleSetElement = document.getDocumentElement(); @@ -494,6 +489,9 @@ public class RuleSetFactory { throws RuleSetNotFoundException { Element ruleElement = (Element) ruleNode; String ref = ruleElement.getAttribute("ref"); + if (compatibilityFilter != null) { + ref = compatibilityFilter.applyRef(ref); + } if (ref.endsWith("xml")) { parseRuleSetReferenceNode(ruleSetBuilder, ruleElement, ref, rulesetReferences); } else if (StringUtils.isBlank(ref)) { 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 6ed1d189a4..d3dadd6f51 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java @@ -4,21 +4,13 @@ package net.sourceforge.pmd; -import java.io.IOException; -import java.io.InputStream; -import java.io.Reader; -import java.io.StringReader; -import java.nio.charset.StandardCharsets; -import java.util.LinkedList; +import java.text.MessageFormat; +import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.apache.commons.io.IOUtils; - -import net.sourceforge.pmd.annotation.InternalApi; +import org.checkerframework.checker.nullness.qual.Nullable; /** * Provides a simple filter mechanism to avoid failing to parse an old ruleset, @@ -26,72 +18,77 @@ import net.sourceforge.pmd.annotation.InternalApi; * renamed or moved to another ruleset. * * @see issue 1360 - * - * @deprecated Use {@link RuleSetLoader#enableCompatibility(boolean)} to enable this feature. - * This implementation is internal API. */ -@InternalApi -@Deprecated -public class RuleSetFactoryCompatibility { +final class RuleSetFactoryCompatibility { + + static final RuleSetFactoryCompatibility INSTANCE = new RuleSetFactoryCompatibility(); + + + static { + // PMD 5.3.0 + INSTANCE.addFilterRuleRenamed("java", "design", "UncommentedEmptyMethod", "UncommentedEmptyMethodBody"); + INSTANCE.addFilterRuleRemoved("java", "controversial", "BooleanInversion"); + + // PMD 5.3.1 + INSTANCE.addFilterRuleRenamed("java", "design", "UseSingleton", "UseUtilityClass"); + + // PMD 5.4.0 + INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyCatchBlock"); + INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyIfStatement"); + INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyWhileStmt"); + INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyTryBlock"); + INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyFinallyBlock"); + INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptySwitchStatements"); + INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptySynchronizedBlock"); + INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyStatementNotInLoop"); + INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyInitializer"); + INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyStatementBlock"); + INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyStaticInitializer"); + INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryConversionTemporary"); + INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryReturn"); + INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryFinalModifier"); + INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UselessOverridingMethod"); + INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UselessOperationOnImmutable"); + INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UnusedNullCheckInEquals"); + INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UselessParentheses"); + + // PMD 5.6.0 + INSTANCE.addFilterRuleRenamed("java", "design", "AvoidConstantsInterface", "ConstantsInInterface"); + // unused/UnusedModifier moved AND renamed, order is important! + INSTANCE.addFilterRuleMovedAndRenamed("java", "unusedcode", "UnusedModifier", "unnecessary", "UnnecessaryModifier"); + + // PMD 6.0.0 + INSTANCE.addFilterRuleMoved("java", "controversial", "unnecessary", "UnnecessaryParentheses"); + INSTANCE.addFilterRuleRenamed("java", "unnecessary", "UnnecessaryParentheses", "UselessParentheses"); + INSTANCE.addFilterRuleMoved("java", "typeresolution", "coupling", "LooseCoupling"); + INSTANCE.addFilterRuleMoved("java", "typeresolution", "clone", "CloneMethodMustImplementCloneable"); + INSTANCE.addFilterRuleMoved("java", "typeresolution", "imports", "UnusedImports"); + INSTANCE.addFilterRuleMoved("java", "typeresolution", "strictexception", "SignatureDeclareThrowsException"); + INSTANCE.addFilterRuleRenamed("java", "naming", "MisleadingVariableName", "MIsLeadingVariableName"); + INSTANCE.addFilterRuleRenamed("java", "unnecessary", "UnnecessaryFinalModifier", "UnnecessaryModifier"); + INSTANCE.addFilterRuleRenamed("java", "empty", "EmptyStaticInitializer", "EmptyInitializer"); + // GuardLogStatementJavaUtil moved and renamed... + INSTANCE.addFilterRuleMovedAndRenamed("java", "logging-java", "GuardLogStatementJavaUtil", "logging-jakarta-commons", "GuardLogStatement"); + INSTANCE.addFilterRuleRenamed("java", "logging-jakarta-commons", "GuardDebugLogging", "GuardLogStatement"); + + } + + private static final Logger LOG = Logger.getLogger(RuleSetFactoryCompatibility.class.getName()); - private List filters = new LinkedList<>(); + private final List filters = new ArrayList<>(); /** * Creates a new instance of the compatibility filter with the built-in * filters for the modified PMD rules. */ - public RuleSetFactoryCompatibility() { - // PMD 5.3.0 - addFilterRuleRenamed("java", "design", "UncommentedEmptyMethod", "UncommentedEmptyMethodBody"); - addFilterRuleRemoved("java", "controversial", "BooleanInversion"); + RuleSetFactoryCompatibility() { - // PMD 5.3.1 - addFilterRuleRenamed("java", "design", "UseSingleton", "UseUtilityClass"); - - // PMD 5.4.0 - addFilterRuleMoved("java", "basic", "empty", "EmptyCatchBlock"); - addFilterRuleMoved("java", "basic", "empty", "EmptyIfStatement"); - addFilterRuleMoved("java", "basic", "empty", "EmptyWhileStmt"); - addFilterRuleMoved("java", "basic", "empty", "EmptyTryBlock"); - addFilterRuleMoved("java", "basic", "empty", "EmptyFinallyBlock"); - addFilterRuleMoved("java", "basic", "empty", "EmptySwitchStatements"); - addFilterRuleMoved("java", "basic", "empty", "EmptySynchronizedBlock"); - addFilterRuleMoved("java", "basic", "empty", "EmptyStatementNotInLoop"); - addFilterRuleMoved("java", "basic", "empty", "EmptyInitializer"); - addFilterRuleMoved("java", "basic", "empty", "EmptyStatementBlock"); - addFilterRuleMoved("java", "basic", "empty", "EmptyStaticInitializer"); - addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryConversionTemporary"); - addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryReturn"); - addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryFinalModifier"); - addFilterRuleMoved("java", "basic", "unnecessary", "UselessOverridingMethod"); - addFilterRuleMoved("java", "basic", "unnecessary", "UselessOperationOnImmutable"); - addFilterRuleMoved("java", "basic", "unnecessary", "UnusedNullCheckInEquals"); - addFilterRuleMoved("java", "basic", "unnecessary", "UselessParentheses"); - - // PMD 5.6.0 - addFilterRuleRenamed("java", "design", "AvoidConstantsInterface", "ConstantsInInterface"); - // unused/UnusedModifier moved AND renamed, order is important! - addFilterRuleMovedAndRenamed("java", "unusedcode", "UnusedModifier", "unnecessary", "UnnecessaryModifier"); - - // PMD 6.0.0 - addFilterRuleMoved("java", "controversial", "unnecessary", "UnnecessaryParentheses"); - addFilterRuleRenamed("java", "unnecessary", "UnnecessaryParentheses", "UselessParentheses"); - addFilterRuleMoved("java", "typeresolution", "coupling", "LooseCoupling"); - addFilterRuleMoved("java", "typeresolution", "clone", "CloneMethodMustImplementCloneable"); - addFilterRuleMoved("java", "typeresolution", "imports", "UnusedImports"); - addFilterRuleMoved("java", "typeresolution", "strictexception", "SignatureDeclareThrowsException"); - addFilterRuleRenamed("java", "naming", "MisleadingVariableName", "MIsLeadingVariableName"); - addFilterRuleRenamed("java", "unnecessary", "UnnecessaryFinalModifier", "UnnecessaryModifier"); - addFilterRuleRenamed("java", "empty", "EmptyStaticInitializer", "EmptyInitializer"); - // GuardLogStatementJavaUtil moved and renamed... - addFilterRuleMovedAndRenamed("java", "logging-java", "GuardLogStatementJavaUtil", "logging-jakarta-commons", "GuardLogStatement"); - addFilterRuleRenamed("java", "logging-jakarta-commons", "GuardDebugLogging", "GuardLogStatement"); } void addFilterRuleMovedAndRenamed(String language, String oldRuleset, String oldName, String newRuleset, String newName) { filters.add(RuleSetFilter.ruleMoved(language, oldRuleset, newRuleset, oldName)); - filters.add(RuleSetFilter.ruleRenamedMoved(language, newRuleset, oldName, newName)); + filters.add(RuleSetFilter.ruleRenamed(language, newRuleset, oldName, newName)); } void addFilterRuleRenamed(String language, String ruleset, String oldName, String newName) { @@ -106,134 +103,102 @@ public class RuleSetFactoryCompatibility { filters.add(RuleSetFilter.ruleRemoved(language, ruleset, name)); } - /** - * Applies all configured filters against the given input stream. The - * resulting reader will contain the original ruleset modified by the - * filters. - * - * @param stream the original ruleset file input stream - * @return a reader, from which the filtered ruleset can be read - * @throws IOException if the stream couldn't be read - */ - public Reader filterRuleSetFile(InputStream stream) throws IOException { - byte[] bytes = IOUtils.toByteArray(stream); - String encoding = determineEncoding(bytes); - String ruleset = new String(bytes, encoding); - ruleset = applyAllFilters(ruleset); - - return new StringReader(ruleset); - } - - private String applyAllFilters(String ruleset) { + public @Nullable String applyRef(String ruleset) { String result = ruleset; for (RuleSetFilter filter : filters) { - result = filter.apply(result); + result = filter.applyRef(result); + if (result == null) { + return null; + } } return result; } - private static final Pattern ENCODING_PATTERN = Pattern.compile("encoding=\"([^\"]+)\""); - /** - * Determines the encoding of the given bytes, assuming this is a XML - * document, which specifies the encoding in the first 1024 bytes. - * - * @param bytes - * the input bytes, might be more or less than 1024 bytes - * @return the determined encoding, falls back to the default UTF-8 encoding - */ - String determineEncoding(byte[] bytes) { - String firstBytes = new String(bytes, 0, bytes.length > 1024 ? 1024 : bytes.length, - StandardCharsets.ISO_8859_1); - Matcher matcher = ENCODING_PATTERN.matcher(firstBytes); - String encoding = StandardCharsets.UTF_8.name(); - if (matcher.find()) { - encoding = matcher.group(1); + public @Nullable String applyExclude(String ref, String excludeName) { + String result = excludeName; + for (RuleSetFilter filter : filters) { + result = filter.applyExclude(ref, result); + if (result == null) { + return null; + } } - return encoding; + return result; } private static class RuleSetFilter { - private final Pattern refPattern; - private final String replacement; - private Pattern exclusionPattern; - private String exclusionReplacement; + + private static final String MOVED_MESSAGE = "The rule \"{1}\" has been moved from ruleset \"{0}\" to \"{2}\". Please change your ruleset!"; + private static final String RENAMED_MESSAGE = "The rule \"{1}\" has been renamed to \"{3}\". Please change your ruleset!"; + private static final String REMOVED_MESSAGE = "The rule \"{1}\" in ruleset \"{0}\" has been removed from PMD and no longer exists. Please change your ruleset!"; + private final String ruleRef; + private final String oldRuleset; + private final String oldName; + private final String newRuleset; + private final String newName; private final String logMessage; - private RuleSetFilter(String refPattern, String replacement, String logMessage) { + private RuleSetFilter(String oldRuleset, + String oldName, + @Nullable String newRuleset, + @Nullable String newName, + String logMessage) { + this.oldRuleset = oldRuleset; + this.oldName = oldName; + this.newRuleset = newRuleset; + this.newName = newName; this.logMessage = logMessage; - if (replacement != null) { - this.refPattern = Pattern.compile("ref=\"" + Pattern.quote(refPattern) + "\""); - this.replacement = "ref=\"" + replacement + "\""; - } else { - this.refPattern = Pattern.compile(""); - this.replacement = ""; - } - } - - private void setExclusionPattern(String oldName, String newName) { - exclusionPattern = Pattern.compile(""); - if (newName != null) { - exclusionReplacement = ""; - } else { - exclusionReplacement = ""; - } + this.ruleRef = oldRuleset + "/" + oldName; } public static RuleSetFilter ruleRenamed(String language, String ruleset, String oldName, String newName) { - RuleSetFilter filter = ruleRenamedMoved(language, ruleset, oldName, newName); - filter.setExclusionPattern(oldName, newName); - return filter; - } - - public static RuleSetFilter ruleRenamedMoved(String language, String ruleset, String oldName, String newName) { - String base = "rulesets/" + language + "/" + ruleset + ".xml/"; - return new RuleSetFilter(base + oldName, base + newName, "The rule \"" + oldName - + "\" has been renamed to \"" + newName + "\". Please change your ruleset!"); + String base = "rulesets/" + language + "/" + ruleset + ".xml"; + return new RuleSetFilter(base, oldName, base, newName, RENAMED_MESSAGE); } public static RuleSetFilter ruleMoved(String language, String oldRuleset, String newRuleset, String ruleName) { String base = "rulesets/" + language + "/"; - return new RuleSetFilter(base + oldRuleset + ".xml/" + ruleName, base + newRuleset + ".xml/" + ruleName, - "The rule \"" + ruleName + "\" has been moved from ruleset \"" + oldRuleset + "\" to \"" - + newRuleset + "\". Please change your ruleset!"); + return new RuleSetFilter(base + oldRuleset + ".xml", ruleName, + base + newRuleset + ".xml", ruleName, + MOVED_MESSAGE); } public static RuleSetFilter ruleRemoved(String language, String ruleset, String name) { - RuleSetFilter filter = new RuleSetFilter("rulesets/" + language + "/" + ruleset + ".xml/" + name, null, - "The rule \"" + name + "\" in ruleset \"" + ruleset - + "\" has been removed from PMD and no longer exists. Please change your ruleset!"); - filter.setExclusionPattern(name, null); - return filter; + String oldRuleset = "rulesets/" + language + "/" + ruleset + ".xml"; + return new RuleSetFilter(oldRuleset, name, + null, null, + REMOVED_MESSAGE); } - String apply(String ruleset) { - String result = ruleset; - Matcher matcher = refPattern.matcher(ruleset); + @Nullable String applyExclude(String ref, String name) { + if (oldRuleset.equals(ref) + && oldName.equals(name) + && oldRuleset.equals(newRuleset)) { + return newName; + } - if (matcher.find()) { - result = matcher.replaceAll(replacement); + return name; + } + + @Nullable String applyRef(String ref) { + + if (ref.equals(this.ruleRef)) { if (LOG.isLoggable(Level.WARNING)) { - LOG.warning("Applying rule set filter: " + logMessage); + String log = MessageFormat.format(logMessage, oldRuleset, oldName, newRuleset, newName); + LOG.warning("Applying rule set filter: " + log); + } + + if (newName != null) { + return newRuleset + "/" + newName; + } else { + // deleted + return null; } } - if (exclusionPattern == null) { - return result; - } - - Matcher exclusions = exclusionPattern.matcher(result); - if (exclusions.find()) { - result = exclusions.replaceAll(exclusionReplacement); - - if (LOG.isLoggable(Level.WARNING)) { - LOG.warning("Applying rule set filter for exclusions: " + logMessage); - } - } - - return result; + return ref; } } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java index 7dfb6e1f09..6b85dcb6f0 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java @@ -6,10 +6,8 @@ package net.sourceforge.pmd; import java.io.ByteArrayInputStream; import java.io.InputStream; -import java.io.Reader; import java.nio.charset.StandardCharsets; -import org.apache.commons.io.IOUtils; import org.junit.Assert; import org.junit.Test; @@ -34,25 +32,15 @@ public class RuleSetFactoryCompatibilityTest { } @Test - public void testCorrectMovedAndRename() throws Exception { - final String ruleset = "\n" + "\n" + "\n" - + " Test\n" + "\n" - + " \n" + "\n"; + public void testCorrectMovedAndRename() { RuleSetFactoryCompatibility rsfc = new RuleSetFactoryCompatibility(); rsfc.addFilterRuleMoved("dummy", "notexisting", "basic", "OldDummyBasicMockRule"); rsfc.addFilterRuleRenamed("dummy", "basic", "OldDummyBasicMockRule", "NewNameForDummyBasicMockRule"); - InputStream stream = new ByteArrayInputStream(ruleset.getBytes(StandardCharsets.ISO_8859_1)); - Reader filtered = rsfc.filterRuleSetFile(stream); - String out = IOUtils.toString(filtered); + String out = rsfc.applyRef("rulesets/dummy/notexisting.xml/OldDummyBasicMockRule"); - Assert.assertFalse(out.contains("notexisting.xml")); - Assert.assertFalse(out.contains("OldDummyBasicMockRule")); - Assert.assertTrue(out.contains("")); + Assert.assertEquals("rulesets/dummy/basic.xml/NewNameForDummyBasicMockRule", out); } @Test @@ -74,84 +62,41 @@ public class RuleSetFactoryCompatibilityTest { } @Test - public void testExclusionRenamedAndMoved() throws Exception { - final String ruleset = "\n" + "\n" + "\n" - + " Test\n" + "\n" - + " \n" - + " \n" - + " \n" - + "\n"; + public void testExclusionRenamedAndMoved() { RuleSetFactoryCompatibility rsfc = new RuleSetFactoryCompatibility(); rsfc.addFilterRuleMovedAndRenamed("dummy", "oldbasic", "OldDummyBasicMockRule", "basic", "NewNameForDummyBasicMockRule"); - InputStream stream = new ByteArrayInputStream(ruleset.getBytes(StandardCharsets.ISO_8859_1)); - Reader filtered = rsfc.filterRuleSetFile(stream); - String out = IOUtils.toString(filtered); + String in = "rulesets/dummy/oldbasic.xml"; + String out = rsfc.applyRef(in); - Assert.assertTrue(out.contains("OldDummyBasicMockRule")); + Assert.assertEquals(in, out); } @Test - public void testFilter() throws Exception { + public void testFilter() { RuleSetFactoryCompatibility rsfc = new RuleSetFactoryCompatibility(); rsfc.addFilterRuleMoved("dummy", "notexisting", "basic", "DummyBasicMockRule"); rsfc.addFilterRuleRemoved("dummy", "basic", "DeletedRule"); rsfc.addFilterRuleRenamed("dummy", "basic", "OldNameOfBasicMockRule", "NewNameOfBasicMockRule"); - String in = "\n" + "\n" + "\n" - + " Test\n" + "\n" - + " \n" - + " \n" - + " \n" + "\n"; - InputStream stream = new ByteArrayInputStream(in.getBytes(StandardCharsets.ISO_8859_1)); - Reader filtered = rsfc.filterRuleSetFile(stream); - String out = IOUtils.toString(filtered); + Assert.assertEquals("rulesets/dummy/basic.xml/DummyBasicMockRule", + rsfc.applyRef("rulesets/dummy/notexisting.xml/DummyBasicMockRule")); - Assert.assertFalse(out.contains("notexisting.xml")); - Assert.assertTrue(out.contains("")); + Assert.assertEquals("rulesets/dummy/basic.xml/NewNameOfBasicMockRule", + rsfc.applyRef("rulesets/dummy/basic.xml/OldNameOfBasicMockRule")); - Assert.assertFalse(out.contains("DeletedRule")); - - Assert.assertFalse(out.contains("OldNameOfBasicMockRule")); - Assert.assertTrue(out.contains("")); + Assert.assertNull(rsfc.applyRef("rulesets/dummy/basic.xml/DeletedRule")); } @Test - public void testExclusionFilter() throws Exception { + public void testExclusionFilter() { RuleSetFactoryCompatibility rsfc = new RuleSetFactoryCompatibility(); rsfc.addFilterRuleRenamed("dummy", "basic", "AnotherOldNameOfBasicMockRule", "NewNameOfBasicMockRule"); - String in = "\n" + "\n" + "\n" - + " Test\n" + "\n" + " \n" - + " \n" + " \n" + "\n"; - InputStream stream = new ByteArrayInputStream(in.getBytes(StandardCharsets.ISO_8859_1)); - Reader filtered = rsfc.filterRuleSetFile(stream); - String out = IOUtils.toString(filtered); + String out = rsfc.applyExclude("rulesets/dummy/basic.xml", "AnotherOldNameOfBasicMockRule"); - Assert.assertFalse(out.contains("OldNameOfBasicMockRule")); - Assert.assertTrue(out.contains("")); - } - - @Test - public void testEncoding() { - RuleSetFactoryCompatibility rsfc = new RuleSetFactoryCompatibility(); - String testString; - - testString = ""; - Assert.assertEquals("ISO-8859-1", rsfc.determineEncoding(testString.getBytes(StandardCharsets.ISO_8859_1))); - - testString = ""; - Assert.assertEquals("UTF-8", rsfc.determineEncoding(testString.getBytes(StandardCharsets.ISO_8859_1))); + Assert.assertEquals("NewNameOfBasicMockRule", out); } private RuleSet createRulesetFromString(final String ruleset, RuleSetFactory factory) From 94028a1ce6289ea794f109308eef672f072215c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 17:38:40 +0100 Subject: [PATCH 282/299] Remove deprecated ctors of RuleSetFactory --- .../net/sourceforge/pmd/RuleSetFactory.java | 48 +------ .../net/sourceforge/pmd/RuleSetLoader.java | 12 +- .../sourceforge/pmd/RulesetsFactoryUtils.java | 130 +----------------- .../sourceforge/pmd/RuleSetFactoryTest.java | 6 +- .../pmd/internal/StageDependencyTest.java | 5 +- .../pmd/docs/RuleDocGeneratorTest.java | 11 +- .../pmd/lang/ast/test/BaseParsingHelper.kt | 2 +- 7 files changed, 22 insertions(+), 192 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 aa010c7c60..bd3069ef67 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -62,58 +62,14 @@ public class RuleSetFactory { private final Map parsedRulesets = new HashMap<>(); - /** - * @deprecated Use a {@link RuleSetLoader} to build a new factory - */ - @Deprecated // to be removed with PMD 7.0.0. - public RuleSetFactory() { - this(new ResourceLoader(), RulePriority.LOW, false, true); - } - - /** - * @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, - final boolean warnDeprecated, final boolean enableCompatibility) { - this(new ResourceLoader(classLoader), minimumPriority, warnDeprecated, enableCompatibility); - } - - /** - * @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, - final boolean warnDeprecated, final boolean enableCompatibility) { - this(resourceLoader, minimumPriority, warnDeprecated, enableCompatibility, false); - } - RuleSetFactory(final ResourceLoader resourceLoader, final RulePriority minimumPriority, - final boolean warnDeprecated, final boolean enableCompatibility, boolean includeDeprecatedRuleReferences) { + final boolean warnDeprecated, final RuleSetFactoryCompatibility compatFilter, boolean includeDeprecatedRuleReferences) { this.resourceLoader = resourceLoader; this.minimumPriority = minimumPriority; this.warnDeprecated = warnDeprecated; this.includeDeprecatedRuleReferences = includeDeprecatedRuleReferences; - if (enableCompatibility) { - this.compatibilityFilter = new RuleSetFactoryCompatibility(); - } else { - this.compatibilityFilter = null; - } - } - - /** - * 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 - * - * @deprecated Use {@link #toLoader()} 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); + this.compatibilityFilter = compatFilter; } 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 36ba54503d..3d5a0173c8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -12,6 +12,8 @@ import java.util.List; import java.util.Properties; import java.util.logging.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; + import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.util.CollectionUtil; @@ -30,7 +32,7 @@ public final class RuleSetLoader { private ResourceLoader resourceLoader = new ResourceLoader(RuleSetLoader.class.getClassLoader()); private RulePriority minimumPriority = RulePriority.LOW; private boolean warnDeprecated = true; - private boolean enableCompatibility = true; + private @Nullable RuleSetFactoryCompatibility compatFilter; private boolean includeDeprecatedRuleReferences = false; /** @@ -81,7 +83,11 @@ public final class RuleSetLoader { * @return This instance, modified */ public RuleSetLoader enableCompatibility(boolean enable) { - this.enableCompatibility = enable; + if (enable) { + this.compatFilter = RuleSetFactoryCompatibility.INSTANCE; + } else { + this.compatFilter = null; + } return this; } @@ -110,7 +116,7 @@ public final class RuleSetLoader { this.resourceLoader, this.minimumPriority, this.warnDeprecated, - this.enableCompatibility, + this.compatFilter, this.includeDeprecatedRuleReferences ); } 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 135736f8a6..cc2b70bf56 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java @@ -11,7 +11,6 @@ import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.benchmark.TimedOperation; import net.sourceforge.pmd.benchmark.TimedOperationCategory; -import net.sourceforge.pmd.util.ResourceLoader; /** * @deprecated Use a {@link RuleSetLoader} instead @@ -79,35 +78,6 @@ public final class RulesetsFactoryUtils { } } - /** - * @deprecated Use a {@link RuleSetLoader} - */ - @InternalApi - @Deprecated - public static RuleSetFactory getRulesetFactory(final PMDConfiguration configuration, - final ResourceLoader resourceLoader) { - return new RuleSetFactory(resourceLoader, configuration.getMinimumPriority(), true, - configuration.isRuleSetFactoryCompatibilityEnabled()); - } - - /** - * Returns a ruleset factory which uses the classloader for PMD - * classes to resolve resource references. - * - * @param configuration PMD configuration, contains info about the - * factory parameters - * - * @return A ruleset factory - * - * @see #createFactory(PMDConfiguration, ClassLoader) - * - * @deprecated Use {@link RuleSetLoader#fromPmdConfig(PMDConfiguration)} - */ - @Deprecated - public static RuleSetFactory createFactory(final PMDConfiguration configuration) { - return createFactory(configuration, RulesetsFactoryUtils.class.getClassLoader()); - } - /** * Returns a ruleset factory with default parameters. It doesn't prune * rules based on priority, and doesn't warn for deprecations. @@ -116,105 +86,9 @@ public final class RulesetsFactoryUtils { * * @see RuleSetLoader */ + @Deprecated public static RuleSetFactory defaultFactory() { - return new RuleSetFactory(); - } - - /** - * Returns a ruleset factory which uses the provided {@link ClassLoader} - * to resolve resource references. It warns for deprecated rule usages. - * - * @param configuration PMD configuration, contains info about the - * factory parameters - * @param classLoader Class loader to load resources - * - * @return A ruleset factory - * - * @see #createFactory(PMDConfiguration) - * - * @deprecated Use a {@link RuleSetLoader} - */ - @Deprecated - public static RuleSetFactory createFactory(final PMDConfiguration configuration, ClassLoader classLoader) { - return createFactory(classLoader, - configuration.getMinimumPriority(), - true, - configuration.isRuleSetFactoryCompatibilityEnabled()); - } - - /** - * Returns a ruleset factory which uses the provided {@link ClassLoader} - * to resolve resource references. - * - * @param minimumPriority Minimum priority for rules to be included - * @param warnDeprecated If true, print warnings when deprecated rules are included - * @param enableCompatibility If true, rule references to moved rules are mapped to their - * new location if they are known - * @param classLoader Class loader to load resources - * - * @return A ruleset factory - * - * @see #createFactory(PMDConfiguration) - * - * @deprecated Use a {@link RuleSetLoader} - */ - @Deprecated - public static RuleSetFactory createFactory(ClassLoader classLoader, - RulePriority minimumPriority, - boolean warnDeprecated, - boolean enableCompatibility) { - - return new RuleSetFactory(new ResourceLoader(classLoader), minimumPriority, warnDeprecated, enableCompatibility); - } - - /** - * Returns a ruleset factory which uses the classloader for PMD - * classes to resolve resource references. - * - * @param minimumPriority Minimum priority for rules to be included - * @param warnDeprecated If true, print warnings when deprecated rules are included - * @param enableCompatibility If true, rule references to moved rules are mapped to their - * new location if they are known - * - * @return A ruleset factory - * - * @see #createFactory(PMDConfiguration) - * - * @deprecated Use a {@link RuleSetLoader} - */ - @Deprecated - public static RuleSetFactory createFactory(RulePriority minimumPriority, - boolean warnDeprecated, - boolean enableCompatibility) { - return new RuleSetFactory(new ResourceLoader(), minimumPriority, warnDeprecated, enableCompatibility); - } - - /** - * Returns a ruleset factory which uses the classloader for PMD - * classes to resolve resource references. - * - * @param minimumPriority Minimum priority for rules to be included - * @param warnDeprecated If true, print warnings when deprecated rules are included - * @param enableCompatibility If true, rule references to moved rules are mapped to their - * new location if they are known - * @param includeDeprecatedRuleReferences If true, deprecated rule references are retained. Usually, these - * references are ignored, since they indicate renamed/moved rules, and the referenced - * rule is often included in the same ruleset. Enabling this might result in - * duplicated rules. - * - * @return A ruleset factory - * - * @see #createFactory(PMDConfiguration) - * @deprecated Use a {@link RuleSetLoader} - */ - @Deprecated - public static RuleSetFactory createFactory(RulePriority minimumPriority, - boolean warnDeprecated, - boolean enableCompatibility, - boolean includeDeprecatedRuleReferences) { - - return new RuleSetFactory(new ResourceLoader(), minimumPriority, warnDeprecated, enableCompatibility, - includeDeprecatedRuleReferences); + return new RuleSetLoader().toFactory(); } /** 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 52e52f4ff5..1abaa6bc43 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -254,12 +254,10 @@ public class RuleSetFactoryTest { *

    When loading this ruleset at a whole for generating the documentation, we should still * include the deprecated rule reference, so that we can create a nice documentation. * - * @throws Exception */ @Test - public void testRuleSetWithDeprecatedRenamedRuleForDoc() throws Exception { - RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.LOW, false, false, true); - RuleSet rs = rsf.createRuleSet(createRuleSetReferenceId("\n" + "\n" + public void testRuleSetWithDeprecatedRenamedRuleForDoc() { + RuleSet rs = new RuleSetLoader().loadFromResource(createRuleSetReferenceId("\n" + "\n" + " ruleset desc\n" + " " + " " diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/internal/StageDependencyTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/internal/StageDependencyTest.java index aa8760c33b..5eb462cd5a 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/internal/StageDependencyTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/internal/StageDependencyTest.java @@ -18,7 +18,6 @@ import net.sourceforge.pmd.PMDException; 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.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -142,9 +141,9 @@ public class StageDependencyTest { private static RuleSets withRules(Rule r, Rule... rs) { List rsets = new ArrayList<>(); - rsets.add(new RuleSetFactory().createSingleRuleRuleSet(r)); + rsets.add(RuleSet.forSingleRule(r)); for (Rule rule : rs) { - rsets.add(new RuleSetFactory().createSingleRuleRuleSet(rule)); + rsets.add(RuleSet.forSingleRule(rule)); } return new RuleSets(rsets); 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 23d91a043d..7d087032ae 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 @@ -21,11 +21,8 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -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; import net.sourceforge.pmd.docs.MockedFileWriter.FileEntry; public class RuleDocGeneratorTest { @@ -59,11 +56,11 @@ public class RuleDocGeneratorTest { } @Test - public void testSingleRuleset() throws RuleSetNotFoundException, IOException { + public void testSingleRuleset() throws IOException { RuleDocGenerator generator = new RuleDocGenerator(writer, root); - RuleSetFactory rsf = RulesetsFactoryUtils.createFactory(RulePriority.LOW, false, false, true); - RuleSet ruleset = rsf.createRuleSet("rulesets/ruledoctest/sample.xml"); + RuleSetLoader rsf = new RuleSetLoader(); + RuleSet ruleset = rsf.loadFromResource("rulesets/ruledoctest/sample.xml"); generator.generate(Arrays.asList(ruleset), Arrays.asList( diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index 418e27aaee..6b5b6aaa91 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -217,7 +217,7 @@ abstract class BaseParsingHelper, T : RootNode ctx.report = report ctx.sourceCodeFile = File(filename) ctx.isIgnoreExceptions = false - val rules = RuleSetFactory().createSingleRuleRuleSet(rule) + val rules = RuleSet.forSingleRule(rule) try { p.sourceCodeProcessor.processSourceCode(StringReader(code), RuleSets(rules), ctx) } catch (e: PMDException) { From 59fff65a857d6eb84f591fc94a9c582f427540f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 17:46:10 +0100 Subject: [PATCH 283/299] Remove more useless things --- .../net/sourceforge/pmd/RuleSetFactory.java | 45 ++----- .../pmd/RuleSetFactoryCompatibility.java | 121 +++++++++++------- .../net/sourceforge/pmd/RuleSetLoader.java | 35 ++++- .../pmd/RuleSetFactoryCompatibilityTest.java | 37 ++---- ...leSetFactoryDuplicatedRuleLoggingTest.java | 15 +-- 5 files changed, 136 insertions(+), 117 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 bd3069ef67..d577828ab6 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -48,7 +47,7 @@ import net.sourceforge.pmd.util.ResourceLoader; @Deprecated public class RuleSetFactory { - private static final Logger LOG = Logger.getLogger(RuleSetFactory.class.getName()); + private static final Logger LOG = Logger.getLogger(RuleSetLoader.class.getName()); private static final String DESCRIPTION = "description"; private static final String UNEXPECTED_ELEMENT = "Unexpected element <"; @@ -62,8 +61,11 @@ public class RuleSetFactory { private final Map parsedRulesets = new HashMap<>(); - RuleSetFactory(final ResourceLoader resourceLoader, final RulePriority minimumPriority, - final boolean warnDeprecated, final RuleSetFactoryCompatibility compatFilter, boolean includeDeprecatedRuleReferences) { + RuleSetFactory(ResourceLoader resourceLoader, + RulePriority minimumPriority, + boolean warnDeprecated, + RuleSetFactoryCompatibility compatFilter, + boolean includeDeprecatedRuleReferences) { this.resourceLoader = resourceLoader; this.minimumPriority = minimumPriority; this.warnDeprecated = warnDeprecated; @@ -73,31 +75,6 @@ public class RuleSetFactory { } - /** - * Gets the compatibility filter in order to adjust it, e.g. add additional - * filters. - * - * @return the {@link RuleSetFactoryCompatibility} - */ - /* package */ RuleSetFactoryCompatibility getCompatibilityFilter() { - return compatibilityFilter; - } - - /** - * Returns an Iterator of RuleSet objects loaded from descriptions from the - * "categories.properties" resource for each Language with Rule support. - * - * @return An Iterator of RuleSet objects. - * - * @throws RuleSetNotFoundException if the ruleset file could not be found - * - * @deprecated Use {@link RuleSetLoader#getStandardRuleSets()} - */ - @Deprecated - public Iterator getRegisteredRuleSets() throws RuleSetNotFoundException { - return toLoader().getStandardRuleSets().iterator(); - } - /** * Create a RuleSets from a comma separated list of RuleSet reference IDs. * This is a convenience method which calls @@ -445,8 +422,9 @@ public class RuleSetFactory { throws RuleSetNotFoundException { Element ruleElement = (Element) ruleNode; String ref = ruleElement.getAttribute("ref"); - if (compatibilityFilter != null) { - ref = compatibilityFilter.applyRef(ref); + ref = compatibilityFilter.applyRef(ref, this.warnDeprecated); + if (ref == null) { + return; // deleted rule } if (ref.endsWith("xml")) { parseRuleSetReferenceNode(ruleSetBuilder, ruleElement, ref, rulesetReferences); @@ -481,7 +459,10 @@ public class RuleSetFactory { if (isElementNode(child, "exclude")) { Element excludeElement = (Element) child; String excludedRuleName = excludeElement.getAttribute("name"); - excludedRulesCheck.add(excludedRuleName); + excludedRuleName = compatibilityFilter.applyExclude(ref, excludedRuleName, this.warnDeprecated); + if (excludedRuleName != null) { + excludedRulesCheck.add(excludedRuleName); + } } else if (isElementNode(child, PRIORITY)) { priority = parseTextNode(child).trim(); } 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 d3dadd6f51..c063157b83 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java @@ -21,55 +21,56 @@ import org.checkerframework.checker.nullness.qual.Nullable; */ final class RuleSetFactoryCompatibility { - static final RuleSetFactoryCompatibility INSTANCE = new RuleSetFactoryCompatibility(); + static final RuleSetFactoryCompatibility EMPTY = new RuleSetFactoryCompatibility(); + static final RuleSetFactoryCompatibility DEFAULT = new RuleSetFactoryCompatibility(); static { // PMD 5.3.0 - INSTANCE.addFilterRuleRenamed("java", "design", "UncommentedEmptyMethod", "UncommentedEmptyMethodBody"); - INSTANCE.addFilterRuleRemoved("java", "controversial", "BooleanInversion"); + DEFAULT.addFilterRuleRenamed("java", "design", "UncommentedEmptyMethod", "UncommentedEmptyMethodBody"); + DEFAULT.addFilterRuleRemoved("java", "controversial", "BooleanInversion"); // PMD 5.3.1 - INSTANCE.addFilterRuleRenamed("java", "design", "UseSingleton", "UseUtilityClass"); + DEFAULT.addFilterRuleRenamed("java", "design", "UseSingleton", "UseUtilityClass"); // PMD 5.4.0 - INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyCatchBlock"); - INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyIfStatement"); - INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyWhileStmt"); - INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyTryBlock"); - INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyFinallyBlock"); - INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptySwitchStatements"); - INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptySynchronizedBlock"); - INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyStatementNotInLoop"); - INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyInitializer"); - INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyStatementBlock"); - INSTANCE.addFilterRuleMoved("java", "basic", "empty", "EmptyStaticInitializer"); - INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryConversionTemporary"); - INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryReturn"); - INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryFinalModifier"); - INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UselessOverridingMethod"); - INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UselessOperationOnImmutable"); - INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UnusedNullCheckInEquals"); - INSTANCE.addFilterRuleMoved("java", "basic", "unnecessary", "UselessParentheses"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyCatchBlock"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyIfStatement"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyWhileStmt"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyTryBlock"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyFinallyBlock"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptySwitchStatements"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptySynchronizedBlock"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyStatementNotInLoop"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyInitializer"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyStatementBlock"); + DEFAULT.addFilterRuleMoved("java", "basic", "empty", "EmptyStaticInitializer"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryConversionTemporary"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryReturn"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UnnecessaryFinalModifier"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UselessOverridingMethod"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UselessOperationOnImmutable"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UnusedNullCheckInEquals"); + DEFAULT.addFilterRuleMoved("java", "basic", "unnecessary", "UselessParentheses"); // PMD 5.6.0 - INSTANCE.addFilterRuleRenamed("java", "design", "AvoidConstantsInterface", "ConstantsInInterface"); + DEFAULT.addFilterRuleRenamed("java", "design", "AvoidConstantsInterface", "ConstantsInInterface"); // unused/UnusedModifier moved AND renamed, order is important! - INSTANCE.addFilterRuleMovedAndRenamed("java", "unusedcode", "UnusedModifier", "unnecessary", "UnnecessaryModifier"); + DEFAULT.addFilterRuleMovedAndRenamed("java", "unusedcode", "UnusedModifier", "unnecessary", "UnnecessaryModifier"); // PMD 6.0.0 - INSTANCE.addFilterRuleMoved("java", "controversial", "unnecessary", "UnnecessaryParentheses"); - INSTANCE.addFilterRuleRenamed("java", "unnecessary", "UnnecessaryParentheses", "UselessParentheses"); - INSTANCE.addFilterRuleMoved("java", "typeresolution", "coupling", "LooseCoupling"); - INSTANCE.addFilterRuleMoved("java", "typeresolution", "clone", "CloneMethodMustImplementCloneable"); - INSTANCE.addFilterRuleMoved("java", "typeresolution", "imports", "UnusedImports"); - INSTANCE.addFilterRuleMoved("java", "typeresolution", "strictexception", "SignatureDeclareThrowsException"); - INSTANCE.addFilterRuleRenamed("java", "naming", "MisleadingVariableName", "MIsLeadingVariableName"); - INSTANCE.addFilterRuleRenamed("java", "unnecessary", "UnnecessaryFinalModifier", "UnnecessaryModifier"); - INSTANCE.addFilterRuleRenamed("java", "empty", "EmptyStaticInitializer", "EmptyInitializer"); + DEFAULT.addFilterRuleMoved("java", "controversial", "unnecessary", "UnnecessaryParentheses"); + DEFAULT.addFilterRuleRenamed("java", "unnecessary", "UnnecessaryParentheses", "UselessParentheses"); + DEFAULT.addFilterRuleMoved("java", "typeresolution", "coupling", "LooseCoupling"); + DEFAULT.addFilterRuleMoved("java", "typeresolution", "clone", "CloneMethodMustImplementCloneable"); + DEFAULT.addFilterRuleMoved("java", "typeresolution", "imports", "UnusedImports"); + DEFAULT.addFilterRuleMoved("java", "typeresolution", "strictexception", "SignatureDeclareThrowsException"); + DEFAULT.addFilterRuleRenamed("java", "naming", "MisleadingVariableName", "MIsLeadingVariableName"); + DEFAULT.addFilterRuleRenamed("java", "unnecessary", "UnnecessaryFinalModifier", "UnnecessaryModifier"); + DEFAULT.addFilterRuleRenamed("java", "empty", "EmptyStaticInitializer", "EmptyInitializer"); // GuardLogStatementJavaUtil moved and renamed... - INSTANCE.addFilterRuleMovedAndRenamed("java", "logging-java", "GuardLogStatementJavaUtil", "logging-jakarta-commons", "GuardLogStatement"); - INSTANCE.addFilterRuleRenamed("java", "logging-jakarta-commons", "GuardDebugLogging", "GuardLogStatement"); + DEFAULT.addFilterRuleMovedAndRenamed("java", "logging-java", "GuardLogStatementJavaUtil", "logging-jakarta-commons", "GuardLogStatement"); + DEFAULT.addFilterRuleRenamed("java", "logging-jakarta-commons", "GuardDebugLogging", "GuardLogStatement"); } @@ -103,11 +104,22 @@ final class RuleSetFactoryCompatibility { filters.add(RuleSetFilter.ruleRemoved(language, ruleset, name)); } + @Nullable String applyRef(String ref) { + return applyRef(ref, false); + } - public @Nullable String applyRef(String ruleset) { - String result = ruleset; + + /** + * Returns the new rule ref, or null if the rule was deleted. Returns + * the argument if no replacement is needed. + * + * @param ref Original ref + * @param warn Whether to output a warning if a replacement is done + */ + public @Nullable String applyRef(String ref, boolean warn) { + String result = ref; for (RuleSetFilter filter : filters) { - result = filter.applyRef(result); + result = filter.applyRef(result, warn); if (result == null) { return null; } @@ -115,11 +127,18 @@ final class RuleSetFactoryCompatibility { return result; } - - public @Nullable String applyExclude(String ref, String excludeName) { + /** + * Returns the new rule name, or null if the rule was deleted. Returns + * the argument if no replacement is needed. + * + * @param rulesetRef Ruleset name + * @param excludeName Original excluded name + * @param warn Whether to output a warning if a replacement is done + */ + public @Nullable String applyExclude(String rulesetRef, String excludeName, boolean warn) { String result = excludeName; for (RuleSetFilter filter : filters) { - result = filter.applyExclude(ref, result); + result = filter.applyExclude(rulesetRef, result, warn); if (result == null) { return null; } @@ -171,23 +190,26 @@ final class RuleSetFactoryCompatibility { REMOVED_MESSAGE); } - @Nullable String applyExclude(String ref, String name) { + @Nullable String applyExclude(String ref, String name, boolean warn) { if (oldRuleset.equals(ref) && oldName.equals(name) && oldRuleset.equals(newRuleset)) { + if (warn) { + warn(); + } + return newName; } return name; } - @Nullable String applyRef(String ref) { + @Nullable String applyRef(String ref, boolean warn) { if (ref.equals(this.ruleRef)) { - if (LOG.isLoggable(Level.WARNING)) { - String log = MessageFormat.format(logMessage, oldRuleset, oldName, newRuleset, newName); - LOG.warning("Applying rule set filter: " + log); + if (warn) { + warn(); } if (newName != null) { @@ -200,5 +222,12 @@ final class RuleSetFactoryCompatibility { return ref; } + + private void warn() { + if (LOG.isLoggable(Level.WARNING)) { + String log = MessageFormat.format(logMessage, oldRuleset, oldName, newRuleset, newName); + LOG.warning("Applying rule set filter: " + log); + } + } } } 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 3d5a0173c8..78620dcad9 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -4,15 +4,17 @@ package net.sourceforge.pmd; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Properties; import java.util.logging.Logger; -import org.checkerframework.checker.nullness.qual.Nullable; +import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -32,7 +34,7 @@ public final class RuleSetLoader { private ResourceLoader resourceLoader = new ResourceLoader(RuleSetLoader.class.getClassLoader()); private RulePriority minimumPriority = RulePriority.LOW; private boolean warnDeprecated = true; - private @Nullable RuleSetFactoryCompatibility compatFilter; + private @NonNull RuleSetFactoryCompatibility compatFilter; private boolean includeDeprecatedRuleReferences = false; /** @@ -83,11 +85,13 @@ public final class RuleSetLoader { * @return This instance, modified */ public RuleSetLoader enableCompatibility(boolean enable) { - if (enable) { - this.compatFilter = RuleSetFactoryCompatibility.INSTANCE; - } else { - this.compatFilter = null; - } + return setCompatibility(enable ? RuleSetFactoryCompatibility.DEFAULT + : RuleSetFactoryCompatibility.EMPTY); + } + + // test only + RuleSetLoader setCompatibility(@NonNull RuleSetFactoryCompatibility filter) { + this.compatFilter = filter; return this; } @@ -137,6 +141,23 @@ public final class RuleSetLoader { return loadFromResource(new RuleSetReferenceId(rulesetPath)); } + /** + * Parses and returns a ruleset from string content. + * + * @param filename The symbolic "file name", for error messages. + * @param rulesetXmlContent Xml file contents + * + * @throws RuleSetLoadException If any error occurs (eg, invalid syntax) + */ + public RuleSet loadFromString(String filename, String rulesetXmlContent) { + return loadFromResource(new RuleSetReferenceId(filename) { + @Override + public InputStream getInputStream(ResourceLoader rl) { + return new ByteArrayInputStream(rulesetXmlContent.getBytes(StandardCharsets.UTF_8)); + } + }); + } + /** * Parses several resources into a list of rulesets. * diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java index 6b85dcb6f0..bbc4a5db96 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryCompatibilityTest.java @@ -4,15 +4,9 @@ package net.sourceforge.pmd; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; - import org.junit.Assert; import org.junit.Test; -import net.sourceforge.pmd.util.ResourceLoader; - public class RuleSetFactoryCompatibilityTest { @Test @@ -24,10 +18,13 @@ public class RuleSetFactoryCompatibilityTest { + " Test\n" + "\n" + " \n" + "\n"; - RuleSetFactory factory = RulesetsFactoryUtils.defaultFactory(); - factory.getCompatibilityFilter().addFilterRuleMoved("dummy", "notexisting", "basic", "DummyBasicMockRule"); + RuleSetFactoryCompatibility compat = new RuleSetFactoryCompatibility(); + compat.addFilterRuleMoved("dummy", "notexisting", "basic", "DummyBasicMockRule"); + + + RuleSetLoader factory = new RuleSetLoader().setCompatibility(compat); + RuleSet createdRuleSet = factory.loadFromString("dummy.xml", ruleset); - RuleSet createdRuleSet = createRulesetFromString(ruleset, factory); Assert.assertNotNull(createdRuleSet.getRuleByName("DummyBasicMockRule")); } @@ -44,7 +41,7 @@ public class RuleSetFactoryCompatibilityTest { } @Test - public void testExclusion() throws Exception { + public void testExclusion() { final String ruleset = "\n" + "\n" + "Test\n" + "\n" + " \n" + " \n" + " \n" + "\n"; - RuleSetFactory factory = RulesetsFactoryUtils.defaultFactory(); - factory.getCompatibilityFilter().addFilterRuleRenamed("dummy", "basic", "OldNameOfSampleXPathRule", - "SampleXPathRule"); + RuleSetFactoryCompatibility compat = new RuleSetFactoryCompatibility(); + compat.addFilterRuleRenamed("dummy", "basic", "OldNameOfSampleXPathRule", "SampleXPathRule"); + + RuleSetLoader factory = new RuleSetLoader().setCompatibility(compat); + RuleSet createdRuleSet = factory.loadFromString("dummy.xml", ruleset); - RuleSet createdRuleSet = createRulesetFromString(ruleset, factory); Assert.assertNotNull(createdRuleSet.getRuleByName("DummyBasicMockRule")); Assert.assertNull(createdRuleSet.getRuleByName("SampleXPathRule")); } @@ -94,18 +92,9 @@ public class RuleSetFactoryCompatibilityTest { RuleSetFactoryCompatibility rsfc = new RuleSetFactoryCompatibility(); rsfc.addFilterRuleRenamed("dummy", "basic", "AnotherOldNameOfBasicMockRule", "NewNameOfBasicMockRule"); - String out = rsfc.applyExclude("rulesets/dummy/basic.xml", "AnotherOldNameOfBasicMockRule"); + String out = rsfc.applyExclude("rulesets/dummy/basic.xml", "AnotherOldNameOfBasicMockRule", false); Assert.assertEquals("NewNameOfBasicMockRule", out); } - private RuleSet createRulesetFromString(final String ruleset, RuleSetFactory factory) - throws RuleSetNotFoundException { - return factory.createRuleSet(new RuleSetReferenceId(null) { - @Override - public InputStream getInputStream(ResourceLoader resourceLoader) throws RuleSetNotFoundException { - return new ByteArrayInputStream(ruleset.getBytes(StandardCharsets.UTF_8)); - } - }); - } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java index 38f58273c5..64bb25fce4 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryDuplicatedRuleLoggingTest.java @@ -22,10 +22,10 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { public LocaleRule localeRule = LocaleRule.en(); @org.junit.Rule - public JavaUtilLoggingRule logging = new JavaUtilLoggingRule(RuleSetFactory.class.getName()); + public JavaUtilLoggingRule logging = new JavaUtilLoggingRule(RuleSetLoader.class.getName()); @Test - public void duplicatedRuleReferenceShouldWarn() throws RuleSetNotFoundException { + public void duplicatedRuleReferenceShouldWarn() { RuleSet ruleset = loadRuleSet("duplicatedRuleReference.xml"); assertEquals(1, ruleset.getRules().size()); @@ -37,7 +37,7 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { } @Test - public void duplicatedRuleReferenceWithOverrideShouldNotWarn() throws RuleSetNotFoundException { + public void duplicatedRuleReferenceWithOverrideShouldNotWarn() { RuleSet ruleset = loadRuleSet("duplicatedRuleReferenceWithOverride.xml"); assertEquals(2, ruleset.getRules().size()); @@ -49,7 +49,7 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { } @Test - public void duplicatedRuleReferenceWithOverrideBeforeShouldNotWarn() throws RuleSetNotFoundException { + public void duplicatedRuleReferenceWithOverrideBeforeShouldNotWarn() { RuleSet ruleset = loadRuleSet("duplicatedRuleReferenceWithOverrideBefore.xml"); assertEquals(2, ruleset.getRules().size()); @@ -61,7 +61,7 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { } @Test - public void multipleDuplicates() throws RuleSetNotFoundException { + public void multipleDuplicates() { RuleSet ruleset = loadRuleSet("multipleDuplicates.xml"); assertEquals(2, ruleset.getRules().size()); @@ -74,8 +74,7 @@ public class RuleSetFactoryDuplicatedRuleLoggingTest { assertTrue(logging.getLog().contains("The ruleset rulesets/dummy/basic.xml is referenced multiple times in \"Custom Rules\".")); } - private RuleSet loadRuleSet(String ruleSetFilename) throws RuleSetNotFoundException { - RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); - return rsf.createRuleSet("net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/" + ruleSetFilename); + private RuleSet loadRuleSet(String ruleSetFilename) { + return new RuleSetLoader().loadFromResource("net/sourceforge/pmd/rulesets/duplicatedRuleLoggingTest/" + ruleSetFilename); } } From 99ee5bdee010f1f07227f6a0b5ec00218166b784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 18:29:50 +0100 Subject: [PATCH 284/299] Update tests --- .../pmd/lang/apex/DefaultRulesetTest.java | 14 +- .../net/sourceforge/pmd/RuleSetLoader.java | 5 +- .../sourceforge/pmd/RuleSetFactoryTest.java | 643 ++++++++---------- 3 files changed, 306 insertions(+), 356 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 e8f53a6d68..45850e4649 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,11 +22,9 @@ public class DefaultRulesetTest { @Rule public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests(); - private RuleSetFactory factory = new RuleSetLoader().enableCompatibility(false).toFactory(); - @Test - public void loadDefaultRuleset() throws Exception { - RuleSet ruleset = factory.createRuleSet("rulesets/apex/ruleset.xml"); + public void loadDefaultRuleset() { + RuleSet ruleset = rulesetLoader().loadFromResource("rulesets/apex/ruleset.xml"); Assert.assertNotNull(ruleset); } @@ -39,7 +37,7 @@ public class DefaultRulesetTest { } @Test - public void loadQuickstartRuleset() throws Exception { + public void loadQuickstartRuleset() { Logger.getLogger(RuleSetFactory.class.getName()).addHandler(new Handler() { @Override public void publish(LogRecord record) { @@ -54,7 +52,11 @@ public class DefaultRulesetTest { public void close() throws SecurityException { } }); - RuleSet ruleset = factory.createRuleSet("rulesets/apex/quickstart.xml"); + RuleSet ruleset = rulesetLoader().loadFromResource("rulesets/apex/quickstart.xml"); Assert.assertNotNull(ruleset); } + + private RuleSetLoader rulesetLoader() { + return new RuleSetLoader().enableCompatibility(false); + } } 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 78620dcad9..af8f96be27 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -34,7 +34,7 @@ public final class RuleSetLoader { private ResourceLoader resourceLoader = new ResourceLoader(RuleSetLoader.class.getClassLoader()); private RulePriority minimumPriority = RulePriority.LOW; private boolean warnDeprecated = true; - private @NonNull RuleSetFactoryCompatibility compatFilter; + private @NonNull RuleSetFactoryCompatibility compatFilter = RuleSetFactoryCompatibility.DEFAULT; private boolean includeDeprecatedRuleReferences = false; /** @@ -130,9 +130,6 @@ public final class RuleSetLoader { * Parses and returns a ruleset from its location. The location may * be a file system path, or a resource path (see {@link #loadResourcesWith(ClassLoader)}). * - *

    This replaces {@link RuleSetFactory#createRuleSet(String)}, - * but does not split commas. - * * @param rulesetPath A reference to a single ruleset * * @throws RuleSetLoadException If any error occurs (eg, invalid syntax, or resource not found) 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 1abaa6bc43..d405f99cb4 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -4,23 +4,23 @@ package net.sourceforge.pmd; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; -import java.io.ByteArrayInputStream; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.HashSet; -import java.util.List; import java.util.Set; import org.apache.commons.lang3.StringUtils; -import org.hamcrest.Matchers; +import org.junit.Assert; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -41,26 +41,21 @@ public class RuleSetFactoryTest { @org.junit.Rule public LocaleRule localeRule = LocaleRule.en(); - @Test - public void testRuleSetFileName() throws RuleSetNotFoundException { - RuleSet rs = loadRuleSet(EMPTY_RULESET); - assertNull("RuleSet file name not expected", rs.getFileName()); + @org.junit.Rule + public JavaUtilLoggingRule logging = new JavaUtilLoggingRule(RuleSetLoader.class.getName()); - RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); - rs = rsf.createRuleSet("net/sourceforge/pmd/TestRuleset1.xml"); + @Test + public void testRuleSetFileName() { + RuleSet rs = new RuleSetLoader().loadFromString("dummyRuleset.xml", EMPTY_RULESET); + assertEquals("dummyRuleset.xml", rs.getFileName()); + + rs = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/TestRuleset1.xml"); assertEquals("wrong RuleSet file name", rs.getFileName(), "net/sourceforge/pmd/TestRuleset1.xml"); } @Test - public void testNoRuleSetFileName() throws RuleSetNotFoundException { - RuleSet rs = loadRuleSet(EMPTY_RULESET); - assertNull("RuleSet file name not expected", rs.getFileName()); - } - - @Test - public void testRefs() throws Exception { - RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); - RuleSet rs = rsf.createRuleSet("net/sourceforge/pmd/TestRuleset1.xml"); + public void testRefs() { + RuleSet rs = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/TestRuleset1.xml"); assertNotNull(rs.getRuleByName("TestRuleRef")); } @@ -70,8 +65,7 @@ public class RuleSetFactoryTest { assertNotNull("Test ruleset not found - can't continue with test!", in); in.close(); - RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); - RuleSets rs = rsf.createRuleSets("net/sourceforge/pmd/rulesets/reference-ruleset.xml"); + RuleSet rs = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/rulesets/reference-ruleset.xml"); // added by referencing a complete ruleset (TestRuleset1.xml) assertNotNull(rs.getRuleByName("MockRule1")); assertNotNull(rs.getRuleByName("MockRule2")); @@ -92,7 +86,7 @@ public class RuleSetFactoryTest { // assert that MockRule2 is only once added to the ruleset, so that it // really // overwrites the configuration inherited from TestRuleset1.xml - assertEquals(1, countRule(rs, "MockRule2")); + assertNotNull(rs.getRuleByName("MockRule2")); Rule mockRule1 = rs.getRuleByName("MockRule1"); assertNotNull(mockRule1); @@ -109,38 +103,27 @@ public class RuleSetFactoryTest { Rule ruleset4Rule1 = rs.getRuleByName("Ruleset4Rule1"); assertNotNull(ruleset4Rule1); assertEquals(5, ruleset4Rule1.getPriority().getPriority()); - assertEquals(1, countRule(rs, "Ruleset4Rule1")); + assertNotNull(rs.getRuleByName("Ruleset4Rule1")); // priority overridden for whole TestRuleset4 group Rule ruleset4Rule2 = rs.getRuleByName("Ruleset4Rule2"); assertNotNull(ruleset4Rule2); assertEquals(2, ruleset4Rule2.getPriority().getPriority()); } - private int countRule(RuleSets rs, String ruleName) { - int count = 0; - for (Rule r : rs.getAllRules()) { - if (ruleName.equals(r.getName())) { - count++; - } - } - return count; - } - - @Test(expected = RuleSetNotFoundException.class) - public void testRuleSetNotFound() throws RuleSetNotFoundException { - RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); - rsf.createRuleSet("fooooo"); + @Test + public void testRuleSetNotFound() { + assertThrows(RuleSetLoadException.class, () -> new RuleSetLoader().loadFromResource("fooooo")); } @Test - public void testCreateEmptyRuleSet() throws RuleSetNotFoundException { + public void testCreateEmptyRuleSet() { RuleSet rs = loadRuleSet(EMPTY_RULESET); assertEquals("test", rs.getName()); assertEquals(0, rs.size()); } @Test - public void testSingleRule() throws RuleSetNotFoundException { + public void testSingleRule() { RuleSet rs = loadRuleSet(SINGLE_RULE); assertEquals(1, rs.size()); Rule r = rs.getRules().iterator().next(); @@ -150,7 +133,7 @@ public class RuleSetFactoryTest { } @Test - public void testMultipleRules() throws RuleSetNotFoundException { + public void testMultipleRules() { RuleSet rs = loadRuleSet(MULTIPLE_RULES); assertEquals(2, rs.size()); Set expected = new HashSet<>(); @@ -162,58 +145,59 @@ public class RuleSetFactoryTest { } @Test - public void testSingleRuleWithPriority() throws RuleSetNotFoundException { + public void testSingleRuleWithPriority() { assertEquals(RulePriority.MEDIUM, loadFirstRule(PRIORITY).getPriority()); } @Test - @SuppressWarnings("unchecked") - public void testProps() throws RuleSetNotFoundException { + public void testProps() { Rule r = loadFirstRule(PROPERTIES); - assertEquals("bar", r.getProperty((PropertyDescriptor) r.getPropertyDescriptor("fooString"))); - assertEquals(new Integer(3), r.getProperty((PropertyDescriptor) r.getPropertyDescriptor("fooInt"))); - assertTrue(r.getProperty((PropertyDescriptor) r.getPropertyDescriptor("fooBoolean"))); - assertEquals(3.0d, r.getProperty((PropertyDescriptor) r.getPropertyDescriptor("fooDouble")), 0.05); + assertEquals("bar", r.getProperty(r.getPropertyDescriptor("fooString"))); + assertEquals(3, r.getProperty(r.getPropertyDescriptor("fooInt"))); + assertEquals(true, r.getProperty(r.getPropertyDescriptor("fooBoolean"))); + assertEquals(3.0d, (Double) r.getProperty(r.getPropertyDescriptor("fooDouble")), 0.05); assertNull(r.getPropertyDescriptor("BuggleFish")); assertNotSame(r.getDescription().indexOf("testdesc2"), -1); } @Test - public void testStringMultiPropertyDefaultDelimiter() throws Exception { - Rule r = loadFirstRule("\n\n Desc\n" - + " \n" - + " Please move your class to the right folder(rest \nfolder)\n" - + " 2\n \n \n "); - PropertyDescriptor> prop = (PropertyDescriptor>) r.getPropertyDescriptor("packageRegEx"); - List values = r.getProperty(prop); - assertEquals(Arrays.asList("com.aptsssss", "com.abc"), values); + public void testStringMultiPropertyDefaultDelimiter() { + Rule r = loadFirstRule( + "\n\n Desc\n" + + " \n" + + " Please move your class to the right folder(rest \nfolder)\n" + + " 2\n \n \n "); + Object propValue = r.getProperty(r.getPropertyDescriptor("packageRegEx")); + + assertEquals(Arrays.asList("com.aptsssss", "com.abc"), propValue); } @Test - public void testStringMultiPropertyDelimiter() throws Exception { + public void testStringMultiPropertyDelimiter() { Rule r = loadFirstRule("\n" + "\n " + " ruleset desc\n " + "\n" + + " instead.\" \n" + + "class=\"net.sourceforge.pmd.lang.rule.XPathRule\" language=\"dummy\">\n" + " Please move your class to the right folder(rest \nfolder)\n" + " 2\n \n \n" + " " + ""); - PropertyDescriptor> prop = (PropertyDescriptor>) r.getPropertyDescriptor("packageRegEx"); - List values = r.getProperty(prop); - assertEquals(Arrays.asList("com.aptsssss", "com.abc"), values); + + Object propValue = r.getProperty(r.getPropertyDescriptor("packageRegEx")); + assertEquals(Arrays.asList("com.aptsssss", "com.abc"), propValue); } @Test - public void testRuleSetWithDeprecatedRule() throws Exception { + public void testRuleSetWithDeprecatedRule() { RuleSet rs = loadRuleSet("\n" + "\n" - + " ruleset desc\n" - + " " - + ""); + + " ruleset desc\n" + + " " + + ""); assertEquals(1, rs.getRules().size()); Rule rule = rs.getRuleByName("DummyBasicMockRule"); assertNotNull(rule); @@ -228,11 +212,11 @@ public class RuleSetFactoryTest { * rule reference should be ignored, so at the end, we only have the new rule name in the ruleset. * This is because the deprecated reference points to a rule in the same ruleset. * - * @throws Exception */ @Test - public void testRuleSetWithDeprecatedButRenamedRule() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetWithDeprecatedButRenamedRule() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" + " " + " " @@ -257,12 +241,16 @@ public class RuleSetFactoryTest { */ @Test public void testRuleSetWithDeprecatedRenamedRuleForDoc() { - RuleSet rs = new RuleSetLoader().loadFromResource(createRuleSetReferenceId("\n" + "\n" - + " ruleset desc\n" - + " " - + " " - + " d\n" + " 2\n" + " " - + "")); + RuleSetLoader loader = new RuleSetLoader().includeDeprecatedRuleReferences(true); + RuleSet rs = loader.loadFromString("", + "\n" + "\n" + + " ruleset desc\n" + + " " + + " " + + " d\n" + + " 2\n" + + " " + + ""); assertEquals(2, rs.getRules().size()); assertNotNull(rs.getRuleByName("NewName")); assertNotNull(rs.getRuleByName("OldName")); @@ -271,12 +259,11 @@ public class RuleSetFactoryTest { /** * This is an example of a custom user ruleset, that references a rule, that has been renamed. * The user should get a deprecation warning. - * - * @throws Exception */ @Test - public void testRuleSetReferencesADeprecatedRenamedRule() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetReferencesADeprecatedRenamedRule() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" + " " + ""); assertEquals(1, rs.getRules().size()); @@ -284,8 +271,8 @@ public class RuleSetFactoryTest { assertNotNull(rule); assertEquals(1, - StringUtils.countMatches(logging.getLog(), - "WARNING: Use Rule name rulesets/dummy/basic.xml/DummyBasicMockRule instead of the deprecated Rule name rulesets/dummy/basic.xml/OldNameOfDummyBasicMockRule.")); + StringUtils.countMatches(logging.getLog(), + "WARNING: Use Rule name rulesets/dummy/basic.xml/DummyBasicMockRule instead of the deprecated Rule name rulesets/dummy/basic.xml/OldNameOfDummyBasicMockRule.")); } /** @@ -301,11 +288,11 @@ public class RuleSetFactoryTest { *

    * In the end, we should get all non-deprecated rules of the referenced ruleset. * - * @throws Exception */ @Test - public void testRuleSetReferencesRulesetWithADeprecatedRenamedRule() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetReferencesRulesetWithADeprecatedRenamedRule() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" + " " + ""); assertEquals(2, rs.getRules().size()); @@ -329,13 +316,14 @@ public class RuleSetFactoryTest { *

    * In the end, we should get all non-deprecated rules of the referenced ruleset. * - * @throws Exception */ @Test - public void testRuleSetReferencesRulesetWithAExcludedDeprecatedRule() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetReferencesRulesetWithAExcludedDeprecatedRule() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" - + " " + ""); + + " " + + ""); assertEquals(2, rs.getRules().size()); assertNotNull(rs.getRuleByName("DummyBasicMockRule")); assertNotNull(rs.getRuleByName("SampleXPathRule")); @@ -353,20 +341,21 @@ public class RuleSetFactoryTest { * since not all rules are deprecated in the referenced ruleset. * Since the rule to be excluded doesn't exist, there should be a warning about that. * - * @throws Exception */ @Test - public void testRuleSetReferencesRulesetWithAExcludedNonExistingRule() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetReferencesRulesetWithAExcludedNonExistingRule() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" - + " " + ""); + + " " + + ""); assertEquals(2, rs.getRules().size()); assertNotNull(rs.getRuleByName("DummyBasicMockRule")); assertNotNull(rs.getRuleByName("SampleXPathRule")); assertEquals(0, - StringUtils.countMatches(logging.getLog(), - "WARNING: Discontinue using Rule rulesets/dummy/basic.xml/DeprecatedRule as it is scheduled for removal from PMD.")); + StringUtils.countMatches(logging.getLog(), + "WARNING: Discontinue using Rule rulesets/dummy/basic.xml/DeprecatedRule as it is scheduled for removal from PMD.")); assertEquals(1, StringUtils.countMatches(logging.getLog(), "WARNING: Unable to exclude rules [NonExistingRule] from ruleset reference rulesets/dummy/basic.xml; perhaps the rule name is misspelled or the rule doesn't exist anymore?")); @@ -377,8 +366,9 @@ public class RuleSetFactoryTest { * considered deprecated and the user should get a deprecation warning for the ruleset. */ @Test - public void testRuleSetReferencesDeprecatedRuleset() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetReferencesDeprecatedRuleset() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" + " " + ""); assertEquals(2, rs.getRules().size()); @@ -386,8 +376,8 @@ public class RuleSetFactoryTest { assertNotNull(rs.getRuleByName("SampleXPathRule")); assertEquals(1, - StringUtils.countMatches(logging.getLog(), - "WARNING: The RuleSet rulesets/dummy/deprecated.xml has been deprecated and will be removed in PMD")); + StringUtils.countMatches(logging.getLog(), + "WARNING: The RuleSet rulesets/dummy/deprecated.xml has been deprecated and will be removed in PMD")); } /** @@ -396,21 +386,22 @@ public class RuleSetFactoryTest { * no warning about deprecation - since the deprecated rules are not used. */ @Test - public void testRuleSetReferencesRulesetWithAMovedRule() throws Exception { - RuleSet rs = loadRuleSetWithDeprecationWarnings("\n" + "\n" + public void testRuleSetReferencesRulesetWithAMovedRule() { + RuleSet rs = loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + " ruleset desc\n" + " " + ""); assertEquals(1, rs.getRules().size()); assertNotNull(rs.getRuleByName("DummyBasic2MockRule")); assertEquals(0, - StringUtils.countMatches(logging.getLog(), - "WARNING: Use Rule name rulesets/dummy/basic.xml/DummyBasicMockRule instead of the deprecated Rule name rulesets/dummy/basic2.xml/DummyBasicMockRule. PMD")); + StringUtils.countMatches(logging.getLog(), + "WARNING: Use Rule name rulesets/dummy/basic.xml/DummyBasicMockRule instead of the deprecated Rule name rulesets/dummy/basic2.xml/DummyBasicMockRule. PMD")); } @Test @SuppressWarnings("unchecked") - public void testXPath() throws RuleSetNotFoundException { + public void testXPath() { Rule r = loadFirstRule(XPATH); PropertyDescriptor xpathProperty = (PropertyDescriptor) r.getPropertyDescriptor("xpath"); assertNotNull("xpath property descriptor", xpathProperty); @@ -418,7 +409,7 @@ public class RuleSetFactoryTest { } @Test - public void testExternalReferenceOverride() throws RuleSetNotFoundException { + public void testExternalReferenceOverride() { Rule r = loadFirstRule(REF_OVERRIDE); assertEquals("TestNameOverride", r.getName()); assertEquals("Test message override", r.getMessage()); @@ -435,14 +426,18 @@ public class RuleSetFactoryTest { } @Test - public void testExternalReferenceOverrideNonExistent() throws RuleSetNotFoundException { - ex.expect(IllegalArgumentException.class); - ex.expectMessage("Cannot set non-existent property 'test4' on Rule TestNameOverride"); - loadFirstRule(REF_OVERRIDE_NONEXISTENT); + public void testExternalReferenceOverrideNonExistent() { + RuleSetLoadException ex = assertCannotParse(REF_OVERRIDE_NONEXISTENT); + + assertThat(ex.getCause().getMessage(), containsString("Cannot set non-existent property 'test4' on Rule TestNameOverride")); + } + + private RuleSetLoadException assertCannotParse(String xmlContent) { + return assertThrows(RuleSetLoadException.class, () -> loadFirstRule(xmlContent)); } @Test - public void testReferenceInternalToInternal() throws RuleSetNotFoundException { + public void testReferenceInternalToInternal() { RuleSet ruleSet = loadRuleSet(REF_INTERNAL_TO_INTERNAL); Rule rule = ruleSet.getRuleByName("MockRuleName"); @@ -453,7 +448,7 @@ public class RuleSetFactoryTest { } @Test - public void testReferenceInternalToInternalChain() throws RuleSetNotFoundException { + public void testReferenceInternalToInternalChain() { RuleSet ruleSet = loadRuleSet(REF_INTERNAL_TO_INTERNAL_CHAIN); Rule rule = ruleSet.getRuleByName("MockRuleName"); @@ -467,7 +462,7 @@ public class RuleSetFactoryTest { } @Test - public void testReferenceInternalToExternal() throws RuleSetNotFoundException { + public void testReferenceInternalToExternal() { RuleSet ruleSet = loadRuleSet(REF_INTERNAL_TO_EXTERNAL); Rule rule = ruleSet.getRuleByName("ExternalRefRuleName"); @@ -478,7 +473,7 @@ public class RuleSetFactoryTest { } @Test - public void testReferenceInternalToExternalChain() throws RuleSetNotFoundException { + public void testReferenceInternalToExternalChain() { RuleSet ruleSet = loadRuleSet(REF_INTERNAL_TO_EXTERNAL_CHAIN); Rule rule = ruleSet.getRuleByName("ExternalRefRuleName"); @@ -492,50 +487,50 @@ public class RuleSetFactoryTest { } @Test - public void testReferencePriority() throws RuleSetNotFoundException { + public void testReferencePriority() { 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)); + RuleSetLoader rsf = config.filterAbovePriority(RulePriority.LOW); + RuleSet ruleSet = rsf.loadFromString("", 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).toFactory(); - ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); + rsf = config.filterAbovePriority(RulePriority.MEDIUM_HIGH); + ruleSet = rsf.loadFromString("", 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).toFactory(); - ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_INTERNAL_CHAIN)); + rsf = config.filterAbovePriority(RulePriority.HIGH); + ruleSet = rsf.loadFromString("", REF_INTERNAL_TO_INTERNAL_CHAIN); assertEquals("Number of Rules", 1, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("MockRuleNameRefRef")); - rsf = config.filterAbovePriority(RulePriority.LOW).toFactory(); - ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN)); + rsf = config.filterAbovePriority(RulePriority.LOW); + ruleSet = rsf.loadFromString("", 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).toFactory(); - ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN)); + rsf = config.filterAbovePriority(RulePriority.MEDIUM_HIGH); + ruleSet = rsf.loadFromString("", 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).toFactory(); - ruleSet = rsf.createRuleSet(createRuleSetReferenceId(REF_INTERNAL_TO_EXTERNAL_CHAIN)); + rsf = config.filterAbovePriority(RulePriority.HIGH); + ruleSet = rsf.loadFromString("", REF_INTERNAL_TO_EXTERNAL_CHAIN); assertEquals("Number of Rules", 1, ruleSet.getRules().size()); assertNotNull(ruleSet.getRuleByName("ExternalRefRuleNameRefRef")); } @Test - public void testOverridePriorityLoadWithMinimum() throws RuleSetNotFoundException { - 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"); + public void testOverridePriorityLoadWithMinimum() { + RuleSetLoader rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(true).enableCompatibility(true); + RuleSet ruleset = rsf.loadFromResource("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()); @@ -546,23 +541,23 @@ public class RuleSetFactoryTest { assertNotNull(ruleset.getRuleByName("SampleXPathRule")); // now, load with default minimum priority - rsf = RulesetsFactoryUtils.defaultFactory(); - ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); + rsf = new RuleSetLoader(); + ruleset = rsf.loadFromResource("net/sourceforge/pmd/rulesets/ruleset-minimum-priority.xml"); assertEquals("Number of Rules", 2, ruleset.getRules().size()); Rule dummyBasicMockRule = ruleset.getRuleByName("DummyBasicMockRule"); assertEquals("Wrong Priority", RulePriority.LOW, dummyBasicMockRule.getPriority()); } @Test - public void testExcludeWithMinimumPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetLoader().filterAbovePriority(RulePriority.HIGH).toFactory(); - RuleSet ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); + public void testExcludeWithMinimumPriority() { + RuleSetLoader rsf = new RuleSetLoader().filterAbovePriority(RulePriority.HIGH); + RuleSet ruleset = rsf.loadFromResource("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 RuleSetLoader().filterAbovePriority(RulePriority.LOW).toFactory(); - ruleset = rsf.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); + rsf = new RuleSetLoader().filterAbovePriority(RulePriority.LOW); + ruleset = rsf.loadFromResource("net/sourceforge/pmd/rulesets/ruleset-minimum-priority-exclusion.xml"); // only one rule, we have excluded one... assertEquals("Number of Rules", 1, ruleset.getRules().size()); // rule is excluded @@ -572,100 +567,101 @@ public class RuleSetFactoryTest { } @Test - public void testOverrideMessage() throws RuleSetNotFoundException { + public void testOverrideMessage() { Rule r = loadFirstRule(REF_OVERRIDE_ORIGINAL_NAME); assertEquals("TestMessageOverride", r.getMessage()); } @Test - public void testOverrideMessageOneElem() throws RuleSetNotFoundException { + public void testOverrideMessageOneElem() { Rule r = loadFirstRule(REF_OVERRIDE_ORIGINAL_NAME_ONE_ELEM); assertEquals("TestMessageOverride", r.getMessage()); } - @Test(expected = IllegalArgumentException.class) - public void testIncorrectExternalRef() throws IllegalArgumentException, RuleSetNotFoundException { - loadFirstRule(REF_MISSPELLED_XREF); + @Test + public void testIncorrectExternalRef() { + assertCannotParse(REF_MISSPELLED_XREF); } @Test - public void testSetPriority() throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false).toFactory(); - assertEquals(0, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); - rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false).toFactory(); - assertEquals(1, rsf.createRuleSet(createRuleSetReferenceId(SINGLE_RULE)).size()); + public void testSetPriority() { + RuleSetLoader rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_HIGH).warnDeprecated(false); + assertEquals(0, rsf.loadFromString("", SINGLE_RULE).size()); + rsf = new RuleSetLoader().filterAbovePriority(RulePriority.MEDIUM_LOW).warnDeprecated(false); + assertEquals(1, rsf.loadFromString("", SINGLE_RULE).size()); } @Test - public void testLanguage() throws RuleSetNotFoundException { + public void testLanguage() { Rule r = loadFirstRule(LANGUAGE); assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME), r.getLanguage()); } - @Test(expected = IllegalArgumentException.class) - public void testIncorrectLanguage() throws RuleSetNotFoundException { - loadFirstRule(INCORRECT_LANGUAGE); + @Test + public void testIncorrectLanguage() { + assertCannotParse(INCORRECT_LANGUAGE); } @Test - public void testMinimumLanguageVersion() throws RuleSetNotFoundException { + public void testMinimumLanguageVersion() { Rule r = loadFirstRule(MINIMUM_LANGUAGE_VERSION); assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.4"), - r.getMinimumLanguageVersion()); + r.getMinimumLanguageVersion()); } @Test - public void testIncorrectMinimumLanguageVersion() throws RuleSetNotFoundException { - ex.expect(IllegalArgumentException.class); - ex.expectMessage(Matchers.containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." - loadFirstRule(INCORRECT_MINIMUM_LANGUAGE_VERSION); - } + public void testIncorrectMinimumLanguageVersion() { + RuleSetLoadException ex = assertCannotParse(INCORRECT_MINIMUM_LANGUAGE_VERSION); + + assertThat(ex.getCause().getMessage(), containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." - @Test(expected = IllegalArgumentException.class) - public void testIncorrectMinimumLanugageVersionWithLanguageSetInJava() throws RuleSetNotFoundException { - loadFirstRule("\n" - + "\n" - + " TODO\n" - + "\n" - + " \n" - + " TODO\n" - + " 2\n" - + " \n" - + "\n" - + ""); } @Test - public void testMaximumLanguageVersion() throws RuleSetNotFoundException { + public void testIncorrectMinimumLanguageVersionWithLanguageSetInJava() { + assertCannotParse("\n" + + "\n" + + " TODO\n" + + "\n" + + " \n" + + " TODO\n" + + " 2\n" + + " \n" + + "\n" + + ""); + } + + @Test + public void testMaximumLanguageVersion() { Rule r = loadFirstRule(MAXIMUM_LANGUAGE_VERSION); assertEquals(LanguageRegistry.getLanguage(DummyLanguageModule.NAME).getVersion("1.7"), - r.getMaximumLanguageVersion()); + r.getMaximumLanguageVersion()); } @Test - public void testIncorrectMaximumLanguageVersion() throws RuleSetNotFoundException { - ex.expect(IllegalArgumentException.class); - ex.expectMessage(Matchers.containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." - loadFirstRule(INCORRECT_MAXIMUM_LANGUAGE_VERSION); - } + public void testIncorrectMaximumLanguageVersion() { + RuleSetLoadException ex = assertCannotParse(INCORRECT_MAXIMUM_LANGUAGE_VERSION); - @Test(expected = IllegalArgumentException.class) - public void testInvertedMinimumMaximumLanguageVersions() throws RuleSetNotFoundException { - loadFirstRule(INVERTED_MINIMUM_MAXIMUM_LANGUAGE_VERSIONS); + assertThat(ex.getCause().getMessage(), containsString("1.0, 1.1, 1.2")); // and not "dummy 1.0, dummy 1.1, ..." } @Test - public void testDirectDeprecatedRule() throws RuleSetNotFoundException { + public void testInvertedMinimumMaximumLanguageVersions() { + assertCannotParse(INCORRECT_MAXIMUM_LANGUAGE_VERSION); + } + + @Test + public void testDirectDeprecatedRule() { Rule r = loadFirstRule(DIRECT_DEPRECATED_RULE); assertNotNull("Direct Deprecated Rule", r); assertTrue(r.isDeprecated()); } @Test - public void testReferenceToDeprecatedRule() throws RuleSetNotFoundException { + public void testReferenceToDeprecatedRule() { Rule r = loadFirstRule(REFERENCE_TO_DEPRECATED_RULE); assertNotNull("Reference to Deprecated Rule", r); assertTrue("Rule Reference", r instanceof RuleReference); @@ -675,7 +671,7 @@ public class RuleSetFactoryTest { } @Test - public void testRuleSetReferenceWithDeprecatedRule() throws RuleSetNotFoundException { + public void testRuleSetReferenceWithDeprecatedRule() { RuleSet ruleSet = loadRuleSet(REFERENCE_TO_RULESET_WITH_DEPRECATED_RULE); assertNotNull("RuleSet", ruleSet); assertFalse("RuleSet empty", ruleSet.getRules().isEmpty()); @@ -689,90 +685,77 @@ public class RuleSetFactoryTest { } @Test - public void testDeprecatedRuleSetReference() throws RuleSetNotFoundException { - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - RuleSet ruleSet = ruleSetFactory.createRuleSet("net/sourceforge/pmd/rulesets/ruleset-deprecated.xml"); + public void testDeprecatedRuleSetReference() { + RuleSet ruleSet = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/rulesets/ruleset-deprecated.xml"); assertEquals(2, ruleSet.getRules().size()); } @Test - public void testExternalReferences() throws RuleSetNotFoundException { + public void testExternalReferences() { RuleSet rs = loadRuleSet(EXTERNAL_REFERENCE_RULE_SET); assertEquals(1, rs.size()); assertEquals(MockRule.class.getName(), rs.getRuleByName("MockRule").getRuleClass()); } @Test - public void testIncludeExcludePatterns() throws RuleSetNotFoundException { + public void testIncludeExcludePatterns() { RuleSet ruleSet = loadRuleSet(INCLUDE_EXCLUDE_RULESET); - assertNotNull("Include patterns", ruleSet.getIncludePatterns()); - assertEquals("Include patterns size", 2, ruleSet.getIncludePatterns().size()); - assertEquals("Include pattern #1", "include1", ruleSet.getIncludePatterns().get(0)); - assertEquals("Include pattern #2", "include2", ruleSet.getIncludePatterns().get(1)); + assertNotNull("Include patterns", ruleSet.getFileInclusions()); + assertEquals("Include patterns size", 2, ruleSet.getFileInclusions().size()); + assertEquals("Include pattern #1", "include1", ruleSet.getFileInclusions().get(0).pattern()); + assertEquals("Include pattern #2", "include2", ruleSet.getFileInclusions().get(1).pattern()); - assertNotNull("Exclude patterns", ruleSet.getExcludePatterns()); - assertEquals("Exclude patterns size", 3, ruleSet.getExcludePatterns().size()); - assertEquals("Exclude pattern #1", "exclude1", ruleSet.getExcludePatterns().get(0)); - assertEquals("Exclude pattern #2", "exclude2", ruleSet.getExcludePatterns().get(1)); - assertEquals("Exclude pattern #3", "exclude3", ruleSet.getExcludePatterns().get(2)); + assertNotNull("Exclude patterns", ruleSet.getFileExclusions()); + assertEquals("Exclude patterns size", 3, ruleSet.getFileExclusions().size()); + assertEquals("Exclude pattern #1", "exclude1", ruleSet.getFileExclusions().get(0).pattern()); + assertEquals("Exclude pattern #2", "exclude2", ruleSet.getFileExclusions().get(1).pattern()); + assertEquals("Exclude pattern #3", "exclude3", ruleSet.getFileExclusions().get(2).pattern()); } /** * Rule reference can't be resolved - ref is used instead of class and the * class is old (pmd 4.3 and not pmd 5). - * - * @throws Exception - * any error */ - @Test(expected = RuleSetNotFoundException.class) - public void testBug1202() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId("\n" + "\n" + @Test + public void testBug1202() { + Assert.assertThrows( + RuleSetLoadException.class, + () -> new RuleSetLoader().loadFromString("", "\n" + "\n" + " \n" + " 1\n" + " \n" + " \n" + " \n" + " \n" + " \n" - + "\n"); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - ruleSetFactory.createRuleSet(ref); + + "\n") + ); } /** * See https://sourceforge.net/p/pmd/bugs/1225/ - * - * @throws Exception - * any error */ @Test - public void testEmptyRuleSetFile() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId("\n" + "\n" - + "\n" - + " PMD Ruleset.\n" + "\n" - + " .*Test.*\n" + "\n" + "\n"); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - RuleSet ruleset = ruleSetFactory.createRuleSet(ref); + public void testEmptyRuleSetFile() { + RuleSet ruleset = new RuleSetLoader().loadFromString("", "\n" + "\n" + + "\n" + + " PMD Ruleset.\n" + "\n" + + " .*Test.*\n" + "\n" + "\n"); assertEquals(0, ruleset.getRules().size()); } /** * See https://github.com/pmd/pmd/issues/782 * Empty ruleset should be interpreted as deprecated. - * - * @throws Exception - * any error */ @Test - public void testEmptyRuleSetReferencedShouldNotBeDeprecated() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId("\n" + "\n" - + "\n" - + " Ruleset which references a empty ruleset\n" + "\n" - + " \n" - + "\n"); - RuleSetFactory ruleSetFactory = new RuleSetLoader().loadResourcesWith(new ResourceLoader()).filterAbovePriority(RulePriority.LOW).warnDeprecated(true).enableCompatibility(true).toFactory(); - RuleSet ruleset = ruleSetFactory.createRuleSet(ref); + public void testEmptyRuleSetReferencedShouldNotBeDeprecated() { + RuleSet ruleset = new RuleSetLoader().loadFromString("", "\n" + "\n" + + "\n" + + " Ruleset which references a empty ruleset\n" + "\n" + + " \n" + + "\n"); assertEquals(0, ruleset.getRules().size()); assertTrue(logging.getLog().isEmpty()); @@ -780,43 +763,36 @@ public class RuleSetFactoryTest { /** * See https://sourceforge.net/p/pmd/bugs/1231/ - * - * @throws Exception - * any error */ - @Test(expected = IllegalArgumentException.class) - public void testWrongRuleNameReferenced() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId("\n" - + "\n" - + " Custom ruleset for tests\n" - + " \n" + "\n"); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - ruleSetFactory.createRuleSet(ref); + @Test + public void testWrongRuleNameReferenced() { + assertCannotParse("\n" + + "\n" + + " Custom ruleset for tests\n" + + " \n" + + "\n"); } /** * Unit test for #1312 see https://sourceforge.net/p/pmd/bugs/1312/ * - * @throws Exception - * any error */ @Test - public void testRuleReferenceWithNameOverridden() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId("\n" - + "\n" - + " PMD Plugin preferences rule set\n" + "\n" - + "\n" + "\n" + "\n" + ""); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - RuleSet rs = ruleSetFactory.createRuleSet(ref); + public void testRuleReferenceWithNameOverridden() { + RuleSet rs = loadRuleSet("\n" + + "\n" + + " PMD Plugin preferences rule set\n" + + "\n" + "\n" + "\n" + + ""); - Rule r = rs.getRules().toArray(new Rule[1])[0]; + Rule r = rs.getRules().iterator().next(); assertEquals("OverriddenDummyBasicMockRule", r.getName()); RuleReference ruleRef = (RuleReference) r; assertEquals("DummyBasicMockRule", ruleRef.getRule().getName()); @@ -827,21 +803,17 @@ public class RuleSetFactoryTest { * *

    See https://github.com/pmd/pmd/issues/1978 - with that, it should not be an error anymore. * - * @throws Exception - * any error */ @Test - public void testWrongRuleNameExcluded() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId( - "\n" + "\n" - + " Custom ruleset for tests\n" - + " \n" - + " \n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - RuleSet ruleset = ruleSetFactory.createRuleSet(ref); + public void testWrongRuleNameExcluded() { + RuleSet ruleset = loadRuleSet("\n" + "\n" + + " Custom ruleset for tests\n" + + " \n" + + " \n" + " \n" + + "\n"); assertEquals(4, ruleset.getRules().size()); } @@ -852,8 +824,6 @@ public class RuleSetFactoryTest { * Currently, if a ruleset is imported twice, the excludes of the first * import are ignored. Duplicated rules are silently ignored. * - * @throws Exception - * any error * @see #1537 Implement * strict ruleset parsing * @see */ @Test - public void testExcludeAndImportTwice() throws Exception { - RuleSetReferenceId ref1 = createRuleSetReferenceId( - "\n" + "\n" - + " Custom ruleset for tests\n" - + " \n" + " \n" - + " \n" + "\n"); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - RuleSet ruleset = ruleSetFactory.createRuleSet(ref1); + public void testExcludeAndImportTwice() { + RuleSet ruleset = loadRuleSet("\n" + "\n" + + " Custom ruleset for tests\n" + + " \n" + + " \n" + + " \n" + "\n"); assertNull(ruleset.getRuleByName("DummyBasicMockRule")); - RuleSetReferenceId ref2 = createRuleSetReferenceId( - "\n" + "\n" - + " Custom ruleset for tests\n" - + " \n" + " \n" - + " \n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory2 = RulesetsFactoryUtils.defaultFactory(); - RuleSet ruleset2 = ruleSetFactory2.createRuleSet(ref2); + RuleSet ruleset2 = loadRuleSet("\n" + "\n" + + " Custom ruleset for tests\n" + + " \n" + + " \n" + + " \n" + " \n" + + "\n"); assertNotNull(ruleset2.getRuleByName("DummyBasicMockRule")); - RuleSetReferenceId ref3 = createRuleSetReferenceId( - "\n" + "\n" - + " Custom ruleset for tests\n" - + " \n" + " \n" - + " \n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory3 = RulesetsFactoryUtils.defaultFactory(); - RuleSet ruleset3 = ruleSetFactory3.createRuleSet(ref3); + RuleSet ruleset3 = loadRuleSet("\n" + "\n" + + " Custom ruleset for tests\n" + + " \n" + + " \n" + + " \n" + " \n" + + "\n"); assertNotNull(ruleset3.getRuleByName("DummyBasicMockRule")); } - @org.junit.Rule - public JavaUtilLoggingRule logging = new JavaUtilLoggingRule(RuleSetFactory.class.getName()); - @Test - public void testMissingRuleSetNameIsWarning() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId( - "\n" + "\n" - + " Custom ruleset for tests\n" - + " \n" - + " \n"); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - ruleSetFactory.createRuleSet(ref); + public void testMissingRuleSetNameIsWarning() { + loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + + " Custom ruleset for tests\n" + + " \n" + + " \n" + ); assertTrue(logging.getLog().contains("RuleSet name is missing.")); } @Test - public void testMissingRuleSetDescriptionIsWarning() throws Exception { - RuleSetReferenceId ref = createRuleSetReferenceId( - "\n" + "\n" - + " \n" - + " \n"); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - ruleSetFactory.createRuleSet(ref); + public void testMissingRuleSetDescriptionIsWarning() { + loadRuleSetWithDeprecationWarnings( + "\n" + "\n" + + " \n" + + " \n" + ); assertTrue(logging.getLog().contains("RuleSet description is missing.")); } @@ -1265,27 +1226,17 @@ public class RuleSetFactoryTest { + "\n" + ""; - private Rule loadFirstRule(String ruleSetXml) throws RuleSetNotFoundException { + private Rule loadFirstRule(String ruleSetXml) { RuleSet rs = loadRuleSet(ruleSetXml); return rs.getRules().iterator().next(); } - private RuleSet loadRuleSet(String ruleSetXml) throws RuleSetNotFoundException { - RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); - return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml)); + private RuleSet loadRuleSet(String ruleSetXml) { + return new RuleSetLoader().loadFromString("dummyRuleset.xml", ruleSetXml); } - private RuleSet loadRuleSetWithDeprecationWarnings(String ruleSetXml) throws RuleSetNotFoundException { - RuleSetFactory rsf = new RuleSetLoader().warnDeprecated(true).enableCompatibility(false).toFactory(); - return rsf.createRuleSet(createRuleSetReferenceId(ruleSetXml)); + private RuleSet loadRuleSetWithDeprecationWarnings(String ruleSetXml) { + return new RuleSetLoader().warnDeprecated(true).enableCompatibility(false).loadFromString("testRuleset.xml", ruleSetXml); } - private static RuleSetReferenceId createRuleSetReferenceId(final String ruleSetXml) { - return new RuleSetReferenceId(null) { - @Override - public InputStream getInputStream(ResourceLoader resourceLoader) throws RuleSetNotFoundException { - return new ByteArrayInputStream(ruleSetXml.getBytes(StandardCharsets.UTF_8)); - } - }; - } } From 14df02e3cff79f9c7a186ae29a63cfc90125f251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 18:50:57 +0100 Subject: [PATCH 285/299] Cleanup more tests --- .../main/java/net/sourceforge/pmd/PMD.java | 36 ------ .../sourceforge/pmd/RulesetsFactoryUtils.java | 13 --- .../pmd/processor/AbstractPMDProcessor.java | 25 ----- .../sourceforge/pmd/RuleSetWriterTest.java | 8 +- .../pmd/SourceCodeProcessorTest.java | 2 +- .../processor/MultiThreadProcessorTest.java | 13 +-- .../pmd/docs/RuleSetResolverTest.java | 15 +-- .../pmd/docs/SidebarGeneratorTest.java | 5 +- .../sourceforge/pmd/RuleSetFactoryTest.java | 6 +- .../pmd/AbstractLanguageVersionTest.java | 21 ++-- .../pmd/AbstractRuleSetFactoryTest.java | 106 +++++++----------- .../pmd/testframework/RuleTst.java | 8 +- .../vf/rule/security/VfUnescapeElTest.java | 3 +- 13 files changed, 69 insertions(+), 192 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 a7836e1a31..5ba2f52cfc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -290,42 +290,6 @@ public class PMD { return context; } - /** - * Run PMD on a list of files using multiple threads - if more than one is - * available - * - * @param configuration - * Configuration - * @param ruleSetFactory - * RuleSetFactory - * @param files - * List of {@link DataSource}s - * @param ctx - * RuleContext - * @param renderers - * List of {@link Renderer}s - * - * @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 - */ - @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 - ctx.getReport().addListener(configuration.getAnalysisCache()); - - final RuleSetFactory silentFactory = ruleSetFactory.toLoader().warnDeprecated(false).toFactory(); - newFileProcessor(configuration).processFiles(silentFactory, files, ctx, renderers); - configuration.getAnalysisCache().persist(); - } - /** * Run PMD using the given configuration. This replaces the other overload. * 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 cc2b70bf56..bbe93b3e31 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java @@ -78,19 +78,6 @@ public final class RulesetsFactoryUtils { } } - /** - * Returns a ruleset factory with default parameters. It doesn't prune - * rules based on priority, and doesn't warn for deprecations. - * - * @return A ruleset factory - * - * @see RuleSetLoader - */ - @Deprecated - public static RuleSetFactory defaultFactory() { - return new RuleSetLoader().toFactory(); - } - /** * If in debug modus, print the names of the rules. * 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 5bcf538f21..7869210e92 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 @@ -18,9 +18,7 @@ import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; -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; @@ -68,22 +66,6 @@ public abstract class AbstractPMDProcessor { return dataSource.getNiceFileName(configuration.isReportShortNames(), configuration.getInputPaths()); } - /** - * Create instances for each rule defined in the ruleset(s) in the - * configuration. Please note, that the returned instances must - * not be used by different threads. Each thread must create its - * own copy of the rules. - * - * @param factory The factory used to create the configured rule sets - * @param report The base report on which to report any configuration errors - * @return the rules within a rulesets - */ - 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) { @@ -112,13 +94,6 @@ public abstract class AbstractPMDProcessor { return brokenRules; } - @Deprecated - public void processFiles(RuleSetFactory ruleSetFactory, List files, RuleContext ctx, - List renderers) { - RuleSets rs = createRuleSets(ruleSetFactory, ctx.getReport()); - processFiles(rs, files, ctx, renderers); - } - @SuppressWarnings("PMD.CloseResource") // the data sources must only be closed after the threads are finished // this is done manually without a try-with-resources 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 6f6e1643dc..63bc53a909 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetWriterTest.java @@ -51,7 +51,7 @@ public class RuleSetWriterTest { */ @Test public void testWrite() throws Exception { - RuleSet braces = RulesetsFactoryUtils.defaultFactory().createRuleSet("net/sourceforge/pmd/TestRuleset1.xml"); + RuleSet braces = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/TestRuleset1.xml"); RuleSet ruleSet = new RuleSetBuilder(new Random().nextLong()) .withName("ruleset") .withDescription("ruleset description") @@ -72,13 +72,11 @@ public class RuleSetWriterTest { */ @Test public void testRuleReferenceOverriddenName() throws Exception { - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - RuleSet rs = ruleSetFactory.createRuleSet("dummy-basic"); - RuleSetReference ruleSetReference = new RuleSetReference("rulesets/dummy/basic.xml"); + RuleSet rs = new RuleSetLoader().loadFromResource("rulesets/dummy/basic.xml"); RuleReference ruleRef = new RuleReference(); ruleRef.setRule(rs.getRuleByName("DummyBasicMockRule")); - ruleRef.setRuleSetReference(ruleSetReference); + ruleRef.setRuleSetReference(new RuleSetReference("rulesets/dummy/basic.xml")); ruleRef.setName("Foo"); // override the name RuleSet ruleSet = RuleSet.forSingleRule(ruleRef); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/SourceCodeProcessorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/SourceCodeProcessorTest.java index 1827d2a49f..6c8a17e462 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/SourceCodeProcessorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/SourceCodeProcessorTest.java @@ -43,7 +43,7 @@ public class SourceCodeProcessorTest { processor = new SourceCodeProcessor(new PMDConfiguration()); sourceCode = new StringReader("test"); Rule rule = new RuleThatThrows(); - rulesets = Arrays.asList(RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule)); + rulesets = Arrays.asList(RuleSet.forSingleRule(rule)); ctx = new RuleContext(); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java index e0a1025e8d..67b82de86f 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/processor/MultiThreadProcessorTest.java @@ -21,9 +21,9 @@ import net.sourceforge.pmd.PMDConfiguration; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Report.ConfigurationError; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.RuleSetFactory; +import net.sourceforge.pmd.RuleSetLoader; +import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.ThreadSafeReportListener; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; @@ -36,13 +36,12 @@ public class MultiThreadProcessorTest { private RuleContext ctx; private MultiThreadProcessor processor; - private RuleSetFactory ruleSetFactory; + private RuleSets ruleSets; private List files; private SimpleReportListener reportListener; public void setUpForTest(final String ruleset) { PMDConfiguration configuration = new PMDConfiguration(); - configuration.setRuleSets(ruleset); configuration.setThreads(2); files = new ArrayList<>(); files.add(new StringDataSource("file1-violation.dummy", "ABC")); @@ -53,7 +52,7 @@ public class MultiThreadProcessorTest { ctx.getReport().addListener(reportListener); processor = new MultiThreadProcessor(configuration); - ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); + ruleSets = new RuleSets(new RuleSetLoader().loadFromResources(ruleset)); } @Test @@ -61,7 +60,7 @@ public class MultiThreadProcessorTest { setUpForTest("rulesets/MultiThreadProcessorTest/dysfunctional.xml"); final SimpleRenderer renderer = new SimpleRenderer(null, null); renderer.start(); - processor.processFiles(ruleSetFactory, files, ctx, Collections.singletonList(renderer)); + processor.processFiles(ruleSets, files, ctx, Collections.singletonList(renderer)); renderer.end(); final Iterator configErrors = renderer.getReport().getConfigurationErrors().iterator(); @@ -77,7 +76,7 @@ public class MultiThreadProcessorTest { @Test public void testRulesThreadSafety() { setUpForTest("rulesets/MultiThreadProcessorTest/basic.xml"); - processor.processFiles(ruleSetFactory, files, ctx, Collections.emptyList()); + processor.processFiles(ruleSets, files, ctx, Collections.emptyList()); // if the rule is not executed, then maybe a // ConcurrentModificationException happened diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java index c2c082c44f..f5bc92714c 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.docs; -import static org.junit.Assert.fail; - import java.nio.file.FileSystems; import java.nio.file.Path; import java.util.ArrayList; @@ -15,13 +13,11 @@ import java.util.List; import org.apache.commons.io.FilenameUtils; import org.junit.Test; -import net.sourceforge.pmd.RuleSetFactory; -import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.RulesetsFactoryUtils; +import net.sourceforge.pmd.RuleSetLoader; public class RuleSetResolverTest { - private static List excludedRulesets = new ArrayList<>(); + private static final List excludedRulesets = new ArrayList<>(); static { excludedRulesets.add(FilenameUtils.normalize("pmd-test/src/main/resources/rulesets/dummy/basic.xml")); @@ -34,13 +30,8 @@ public class RuleSetResolverTest { filterRuleSets(additionalRulesets); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); for (String filename : additionalRulesets) { - try { - ruleSetFactory.createRuleSet(filename); - } catch (RuntimeException | RuleSetNotFoundException e) { - fail("Couldn't load ruleset " + filename + ": " + e.getMessage()); - } + new RuleSetLoader().loadFromResource(filename); // will throw if invalid } } diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java index ea01604b3c..97e562e49b 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/SidebarGeneratorTest.java @@ -25,7 +25,6 @@ import org.yaml.snakeyaml.DumperOptions.LineBreak; import org.yaml.snakeyaml.Yaml; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; @@ -40,8 +39,8 @@ public class SidebarGeneratorTest { @Test public void testSidebar() throws IOException { Map> rulesets = new HashMap<>(); - RuleSet ruleSet1 = RulesetsFactoryUtils.defaultFactory().createNewRuleSet("test", "test", "bestpractices.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); - RuleSet ruleSet2 = RulesetsFactoryUtils.defaultFactory().createNewRuleSet("test2", "test", "codestyle.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + RuleSet ruleSet1 = RuleSet.create("test", "test", "bestpractices.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); + RuleSet ruleSet2 = RuleSet.create("test2", "test", "codestyle.xml", Collections.emptyList(), Collections.emptyList(), Collections.emptyList()); rulesets.put(LanguageRegistry.findLanguageByTerseName("java"), Arrays.asList(ruleSet1, ruleSet2)); rulesets.put(LanguageRegistry.findLanguageByTerseName("ecmascript"), Arrays.asList(ruleSet1)); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java index c3ba3904f0..4f8ed3223a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -14,8 +14,8 @@ import org.junit.Test; public class RuleSetFactoryTest extends AbstractRuleSetFactoryTest { @Test - public void testExclusionOfUselessParantheses() throws RuleSetNotFoundException { - RuleSetReferenceId ref = createRuleSetReferenceId( + public void testExclusionOfUselessParantheses() { + RuleSet ruleset = new RuleSetLoader().loadFromString("", "\n" + "Custom ruleset for tests\n" + " \n" + " \n" + " \n" + "\n"); - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - RuleSet ruleset = ruleSetFactory.createRuleSet(ref); Rule rule = ruleset.getRuleByName("UselessParentheses"); assertNull(rule); } diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java index f1a4417601..8834636264 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java @@ -19,7 +19,6 @@ import net.sourceforge.pmd.ant.SourceLanguage; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.util.ResourceLoader; /** * Base test class for {@link LanguageVersion} implementations.
    @@ -101,13 +100,12 @@ public class AbstractLanguageVersionTest { return; } - ResourceLoader rl = new ResourceLoader(); Properties props = new Properties(); String rulesetsProperties = "category/" + simpleTerseName + "/categories.properties"; - try (InputStream inputStream = rl.loadClassPathResourceAsStreamOrThrow(rulesetsProperties)) { + try (InputStream inputStream = getClass().getResourceAsStream(rulesetsProperties)) { props.load(inputStream); } - assertRulesetsAndCategoriesProperties(rl, props); + assertRulesetsAndCategoriesProperties(props); } /** @@ -123,16 +121,15 @@ public class AbstractLanguageVersionTest { return; } - ResourceLoader rl = new ResourceLoader(); Properties props = new Properties(); String rulesetsProperties = "rulesets/" + simpleTerseName + "/rulesets.properties"; - InputStream inputStream = rl.loadClassPathResourceAsStream(rulesetsProperties); + InputStream inputStream = getClass().getResourceAsStream(rulesetsProperties); if (inputStream != null) { // rulesets.properties file exists try (InputStream in = inputStream) { props.load(in); } - assertRulesetsAndCategoriesProperties(rl, props); + assertRulesetsAndCategoriesProperties(props); } } @@ -155,12 +152,11 @@ public class AbstractLanguageVersionTest { + " in the language versions of its language", 1, count); } - private void assertRulesetsAndCategoriesProperties(ResourceLoader rl, Properties props) - throws IOException, RuleSetNotFoundException { + private void assertRulesetsAndCategoriesProperties(Properties props) throws IOException { String rulesetFilenames = props.getProperty("rulesets.filenames"); assertNotNull(rulesetFilenames); - RuleSetFactory factory = RulesetsFactoryUtils.defaultFactory(); + RuleSetLoader factory = new RuleSetLoader(); if (rulesetFilenames.trim().isEmpty()) { return; @@ -168,10 +164,7 @@ public class AbstractLanguageVersionTest { String[] rulesets = rulesetFilenames.split(","); for (String r : rulesets) { - try (InputStream stream = rl.loadClassPathResourceAsStream(r)) { - assertNotNull(stream); - } - RuleSet ruleset = factory.createRuleSet(r); + RuleSet ruleset = factory.loadFromResource(r); assertNotNull(ruleset); } } 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 fcccba55a0..1e5761a3cf 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java @@ -12,10 +12,10 @@ import static org.junit.Assert.fail; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; import java.io.IOException; 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; @@ -27,7 +27,6 @@ import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import java.util.regex.Pattern; -import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; @@ -45,7 +44,6 @@ import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.rule.RuleReference; import net.sourceforge.pmd.lang.rule.XPathRule; import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.util.ResourceLoader; /** * Base test class to verify the language's rulesets. This class should be @@ -55,7 +53,6 @@ public abstract class AbstractRuleSetFactoryTest { @org.junit.Rule public final SystemErrRule systemErrRule = new SystemErrRule().enableLog().muteForSuccessfulTests(); - private static SAXParserFactory saxParserFactory; private static ValidateDefaultHandler validateDefaultHandler; private static SAXParser saxParser; @@ -85,7 +82,7 @@ public abstract class AbstractRuleSetFactoryTest { */ @BeforeClass public static void init() throws Exception { - saxParserFactory = SAXParserFactory.newInstance(); + SAXParserFactory saxParserFactory = SAXParserFactory.newInstance(); saxParserFactory.setValidating(true); saxParserFactory.setNamespaceAware(true); @@ -272,7 +269,7 @@ public abstract class AbstractRuleSetFactoryTest { } // Gets all test PMD Ruleset XML files - private List getRuleSetFileNames() throws IOException, RuleSetNotFoundException { + private List getRuleSetFileNames() throws IOException { List result = new ArrayList<>(); for (Language language : LanguageRegistry.getLanguages()) { @@ -285,33 +282,32 @@ public abstract class AbstractRuleSetFactoryTest { return result; } - private List getRuleSetFileNames(String language) throws IOException, RuleSetNotFoundException { + private List getRuleSetFileNames(String language) throws IOException { List ruleSetFileNames = new ArrayList<>(); - try { - Properties properties = new Properties(); - try (InputStream is = new ResourceLoader().loadClassPathResourceAsStreamOrThrow("rulesets/" + language + "/rulesets.properties")) { - properties.load(is); - } - String fileNames = properties.getProperty("rulesets.filenames"); - StringTokenizer st = new StringTokenizer(fileNames, ","); - while (st.hasMoreTokens()) { - ruleSetFileNames.add(st.nextToken()); - } - } catch (RuleSetNotFoundException e) { + Properties properties = new Properties(); + InputStream input = getClass().getResourceAsStream("rulesets/" + language + "/rulesets.properties"); + if (input == null) { // this might happen if a language is only support by CPD, but not // by PMD System.err.println("No ruleset found for language " + language); } + try (InputStream is = input) { + properties.load(is); + } + String fileNames = properties.getProperty("rulesets.filenames"); + StringTokenizer st = new StringTokenizer(fileNames, ","); + while (st.hasMoreTokens()) { + ruleSetFileNames.add(st.nextToken()); + } + return ruleSetFileNames; } - private RuleSet loadRuleSetByFileName(String ruleSetFileName) throws RuleSetNotFoundException { - RuleSetFactory rsf = RulesetsFactoryUtils.defaultFactory(); - return rsf.createRuleSet(ruleSetFileName); + private RuleSet loadRuleSetByFileName(String ruleSetFileName) { + return new RuleSetLoader().loadFromResource(ruleSetFileName); } - private boolean validateAgainstSchema(String fileName) - throws IOException, RuleSetNotFoundException, ParserConfigurationException, SAXException { + private boolean validateAgainstSchema(String fileName) throws IOException, SAXException { try (InputStream inputStream = loadResourceAsStream(fileName)) { boolean valid = validateAgainstSchema(inputStream); if (!valid) { @@ -321,16 +317,14 @@ public abstract class AbstractRuleSetFactoryTest { } } - private boolean validateAgainstSchema(InputStream inputStream) - throws IOException, RuleSetNotFoundException, ParserConfigurationException, SAXException { + private boolean validateAgainstSchema(InputStream inputStream) throws IOException, SAXException { saxParser.parse(inputStream, validateDefaultHandler.resetValid()); inputStream.close(); return validateDefaultHandler.isValid(); } - private boolean validateAgainstDtd(String fileName) - throws IOException, RuleSetNotFoundException, ParserConfigurationException, SAXException { + private boolean validateAgainstDtd(String fileName) throws IOException, SAXException { try (InputStream inputStream = loadResourceAsStream(fileName)) { boolean valid = validateAgainstDtd(inputStream); if (!valid) { @@ -340,8 +334,7 @@ public abstract class AbstractRuleSetFactoryTest { } } - private boolean validateAgainstDtd(InputStream inputStream) - throws IOException, RuleSetNotFoundException, ParserConfigurationException, SAXException { + private boolean validateAgainstDtd(InputStream inputStream) throws IOException, SAXException { // Read file into memory String file = readFullyToString(inputStream); @@ -381,12 +374,11 @@ public abstract class AbstractRuleSetFactoryTest { } } - private static InputStream loadResourceAsStream(String resource) throws RuleSetNotFoundException { - return new ResourceLoader().loadClassPathResourceAsStreamOrThrow(resource); + private InputStream loadResourceAsStream(String resource) { + return getClass().getResourceAsStream(resource); } - private void testRuleSet(String fileName) - throws IOException, RuleSetNotFoundException, ParserConfigurationException, SAXException { + private void testRuleSet(String fileName) throws IOException, SAXException { // Load original XML // String xml1 = @@ -405,8 +397,8 @@ public abstract class AbstractRuleSetFactoryTest { // System.out.println("xml2: " + xml2); // Read RuleSet from XML, first time - RuleSetFactory ruleSetFactory = RulesetsFactoryUtils.defaultFactory(); - RuleSet ruleSet2 = ruleSetFactory.createRuleSet(createRuleSetReferenceId(xml2)); + RuleSetLoader loader = new RuleSetLoader(); + RuleSet ruleSet2 = loader.loadFromString("", xml2); // Do write/read a 2nd time, just to be sure @@ -419,7 +411,7 @@ public abstract class AbstractRuleSetFactoryTest { // System.out.println("xml3: " + xml3); // Read RuleSet from XML, second time - RuleSet ruleSet3 = ruleSetFactory.createRuleSet(createRuleSetReferenceId(xml3)); + RuleSet ruleSet3 = loader.loadFromString("", xml3); // The 2 written XMLs should all be valid w.r.t Schema/DTD assertTrue("1st roundtrip RuleSet XML is not valid against Schema (filename: " + fileName + ")", @@ -446,10 +438,10 @@ public abstract class AbstractRuleSetFactoryTest { private void assertEqualsRuleSet(String message, RuleSet ruleSet1, RuleSet ruleSet2) { assertEquals(message + ", RuleSet name", ruleSet1.getName(), ruleSet2.getName()); assertEquals(message + ", RuleSet description", ruleSet1.getDescription(), ruleSet2.getDescription()); - assertEquals(message + ", RuleSet exclude patterns", ruleSet1.getExcludePatterns(), - ruleSet2.getExcludePatterns()); - assertEquals(message + ", RuleSet include patterns", ruleSet1.getIncludePatterns(), - ruleSet2.getIncludePatterns()); + assertEquals(message + ", RuleSet exclude patterns", ruleSet1.getFileExclusions(), + ruleSet2.getFileExclusions()); + assertEquals(message + ", RuleSet include patterns", ruleSet1.getFileInclusions(), + ruleSet2.getFileInclusions()); assertEquals(message + ", RuleSet rule count", ruleSet1.getRules().size(), ruleSet2.getRules().size()); for (int i = 0; i < ruleSet1.getRules().size(); i++) { @@ -511,22 +503,6 @@ public abstract class AbstractRuleSetFactoryTest { } } - /** - * Create a {@link RuleSetReferenceId} by the given XML string. - * - * @param ruleSetXml - * the ruleset file content as string - * @return the {@link RuleSetReferenceId} - */ - protected static RuleSetReferenceId createRuleSetReferenceId(final String ruleSetXml) { - return new RuleSetReferenceId(null) { - @Override - public InputStream getInputStream(ResourceLoader resourceLoader) throws RuleSetNotFoundException { - return new ByteArrayInputStream(ruleSetXml.getBytes(StandardCharsets.UTF_8)); - } - }; - } - /** * Validator for the SAX parser */ @@ -550,17 +526,17 @@ public abstract class AbstractRuleSetFactoryTest { } @Override - public void error(SAXParseException e) throws SAXException { + public void error(SAXParseException e) { log("Error", e); } @Override - public void fatalError(SAXParseException e) throws SAXException { + public void fatalError(SAXParseException e) { log("FatalError", e); } @Override - public void warning(SAXParseException e) throws SAXException { + public void warning(SAXParseException e) { log("Warning", e); } @@ -571,17 +547,15 @@ public abstract class AbstractRuleSetFactoryTest { } @Override - public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException { + public InputSource resolveEntity(String publicId, String systemId) throws IOException { String resource = schemaMapping.get(systemId); if (resource != null) { - try { - InputStream inputStream = loadResourceAsStream(resource); - return new InputSource(inputStream); - } catch (RuleSetNotFoundException e) { - System.err.println(e.getMessage()); - throw new IOException(e.getMessage()); + InputStream inputStream = getClass().getResourceAsStream(resource); + if (inputStream == null) { + throw new FileNotFoundException(resource); } + return new InputSource(inputStream); } throw new IllegalArgumentException( "No clue how to handle: publicId=" + publicId + ", systemId=" + systemId); diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java index b6e3188d81..8861452344 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java @@ -40,10 +40,10 @@ import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetNotFoundException; +import net.sourceforge.pmd.RuleSetLoadException; +import net.sourceforge.pmd.RuleSetLoader; import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.RulesetsFactoryUtils; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; @@ -100,14 +100,14 @@ public abstract class RuleTst { */ public Rule findRule(String ruleSet, String ruleName) { try { - Rule rule = RulesetsFactoryUtils.defaultFactory().createRuleSets(ruleSet).getRuleByName(ruleName); + Rule rule = new RuleSetLoader().loadFromResource(ruleSet).getRuleByName(ruleName); if (rule == null) { fail("Rule " + ruleName + " not found in ruleset " + ruleSet); } else { rule.setRuleSetName(ruleSet); } return rule; - } catch (RuleSetNotFoundException e) { + } catch (RuleSetLoadException e) { e.printStackTrace(); fail("Couldn't find ruleset " + ruleSet); return null; 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 5a407676a8..e58545d614 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 @@ -24,7 +24,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; @@ -155,7 +154,7 @@ public class VfUnescapeElTest extends PmdRuleTst { ctx.setSourceCodeFile(vfPagePath.toFile()); ctx.setLanguageVersion(languageVersion); ctx.setIgnoreExceptions(false); - RuleSet rules = RulesetsFactoryUtils.defaultFactory().createSingleRuleRuleSet(rule); + RuleSet rules = RuleSet.forSingleRule(rule); p.getSourceCodeProcessor().processSourceCode(new FileReader(vfPagePath.toFile()), new RuleSets(rules), ctx); // END Based on RuleTst class From 75e4537d0d89964d7076a2929c502e67dac3843d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 19:05:16 +0100 Subject: [PATCH 286/299] Delete RulesetsFactoryUtils --- .../main/java/net/sourceforge/pmd/PMD.java | 45 ++++++- .../net/sourceforge/pmd/RuleSetFactory.java | 123 +----------------- .../sourceforge/pmd/RulesetsFactoryUtils.java | 93 ------------- .../pmd/ant/internal/PMDTaskImpl.java | 15 ++- .../properties/PropertyDescriptorField.java | 3 - .../sourceforge/pmd/RuleSetSchemaTest.java | 2 +- .../pmd/lang/java/PMD5RulesetTest.java | 6 +- .../pmd/lang/java/QuickstartRulesetTest.java | 14 +- 8 files changed, 61 insertions(+), 240 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java 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 5ba2f52cfc..e514ea24cc 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -219,8 +219,9 @@ public class PMD { public static int doPMD(final PMDConfiguration configuration) { // Load the RuleSets - final RuleSetFactory ruleSetFactory = RuleSetLoader.fromPmdConfig(configuration).toFactory(); - final RuleSets ruleSets = RulesetsFactoryUtils.getRuleSetsWithBenchmark(configuration.getRuleSets(), ruleSetFactory); + final RuleSetLoader ruleSetFactory = RuleSetLoader.fromPmdConfig(configuration); + List rulesetPaths = Arrays.asList(configuration.getRuleSets().split(",")); + final RuleSets ruleSets = new RuleSets(getRuleSetsWithBenchmark(rulesetPaths, ruleSetFactory)); if (ruleSets == null) { return PMDCommandLineInterface.NO_ERRORS_STATUS; } @@ -263,13 +264,51 @@ public class PMD { * Make sure it's our own classloader before attempting to close it.... * Maven + Jacoco provide us with a cloaseable classloader that if closed * will throw a ClassNotFoundException. - */ + */ if (configuration.getClassLoader() instanceof ClasspathClassLoader) { IOUtil.tryCloseClassLoader(configuration.getClassLoader()); } } } + private static List getRuleSetsWithBenchmark(List rulesetPaths, RuleSetLoader factory) { + try (TimedOperation to = TimeTracker.startOperation(TimedOperationCategory.LOAD_RULES)) { + List ruleSets = null; + try { + ruleSets = factory.loadFromResources(rulesetPaths); + printRuleNamesInDebug(ruleSets); + if (isEmpty(ruleSets)) { + String msg = "No rules found. Maybe you misspelled a rule name? (" + rulesetPaths + ')'; + LOG.log(Level.SEVERE, msg); + throw new IllegalArgumentException(msg); + } + } catch (RuleSetLoadException rsnfe) { + LOG.log(Level.SEVERE, "Ruleset not found", rsnfe); + throw rsnfe; + } + return ruleSets; + } + } + + private static boolean isEmpty(List rsets) { + return rsets.stream().noneMatch(it -> it.size() > 0); + } + + /** + * If in debug modus, print the names of the rules. + * + * @param rulesets the RuleSets to print + */ + private static void printRuleNamesInDebug(List rulesets) { + if (LOG.isLoggable(Level.FINER)) { + for (RuleSet rset : rulesets) { + for (Rule r : rset.getRules()) { + LOG.finer("Loaded rule " + r.getName()); + } + } + } + } + /** * Creates a new rule context, initialized with a new, empty report. * 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 d577828ab6..3fca157b12 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -40,12 +39,8 @@ import net.sourceforge.pmd.util.ResourceLoader; * RuleSetFactory is responsible for creating RuleSet instances from XML * content. See {@link RuleSetLoader} for configuration options and * their defaults. - * - * @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 { +final class RuleSetFactory { private static final Logger LOG = Logger.getLogger(RuleSetLoader.class.getName()); @@ -75,74 +70,6 @@ public class RuleSetFactory { } - /** - * 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 referenceString - * A comma separated list of RuleSet reference IDs. - * @return The new RuleSets. - * @throws RuleSetNotFoundException - * if unable to find a resource. - * - * @deprecated Use {@link RuleSetLoader#loadFromResource(String)}, - * but note that that method does not split on commas - */ - @Deprecated - public RuleSets createRuleSets(String referenceString) throws RuleSetNotFoundException { - return createRuleSets(RuleSetReferenceId.parse(referenceString)); - } - - /** - * Create a RuleSets from a list of RuleSetReferenceIds. The currently - * configured ResourceLoader is used. - * - * @param ruleSetReferenceIds - * The List of RuleSetReferenceId of the RuleSets to create. - * @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 { - List ruleSets = new ArrayList<>(); - for (RuleSetReferenceId ruleSetReferenceId : ruleSetReferenceIds) { - RuleSet ruleSet = createRuleSet(ruleSetReferenceId); - ruleSets.add(ruleSet); - } - return new RuleSets(ruleSets); - } - - /** - * Create a RuleSet from a RuleSet reference ID string. This is a - * convenience method which calls {@link RuleSetReferenceId#parse(String)}, - * gets the first item in the List, and then calls - * {@link #createRuleSet(RuleSetReferenceId)}. The currently configured - * ResourceLoader is used. - * - * @param referenceString - * A comma separated list of RuleSet reference IDs. - * @return A new RuleSet. - * @throws RuleSetNotFoundException - * if unable to find a resource. - * - * @deprecated Use {@link RuleSetLoader#loadFromResource(String)} and discard the rest of the list. - */ - @Deprecated - public RuleSet createRuleSet(String referenceString) throws RuleSetNotFoundException { - List references = RuleSetReferenceId.parse(referenceString); - if (references.isEmpty()) { - throw new RuleSetNotFoundException( - "No RuleSetReferenceId can be parsed from the string: <" + referenceString + '>'); - } - return createRuleSet(references.get(0)); - } - /** * Create a RuleSet from a RuleSetReferenceId. Priority filtering is ignored * when loading a single Rule. The currently configured ResourceLoader is used. @@ -152,11 +79,8 @@ 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 { + RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { return createRuleSet(ruleSetReferenceId, includeDeprecatedRuleReferences); } @@ -185,49 +109,6 @@ public class RuleSetFactory { 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, if any is not a valid regular expression, it will be ignored - * @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) { - return RuleSet.create(name, description, fileName, toPatterns(excludePatterns), toPatterns(includePatterns), rules); - } - - private Collection toPatterns(Collection sources) { - List result = new ArrayList<>(); - for (String s : sources) { - try { - result.add(Pattern.compile(s)); - } catch (PatternSyntaxException ignored) { - - } - } - return result; - } - /** * Creates a new RuleSet containing a single rule. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java b/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java deleted file mode 100644 index bbe93b3e31..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RulesetsFactoryUtils.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd; - -import java.util.logging.Level; -import java.util.logging.Logger; - -import net.sourceforge.pmd.annotation.InternalApi; -import net.sourceforge.pmd.benchmark.TimeTracker; -import net.sourceforge.pmd.benchmark.TimedOperation; -import net.sourceforge.pmd.benchmark.TimedOperationCategory; - -/** - * @deprecated Use a {@link RuleSetLoader} instead - */ -@Deprecated -public final class RulesetsFactoryUtils { - - private static final Logger LOG = Logger.getLogger(RulesetsFactoryUtils.class.getName()); - - private RulesetsFactoryUtils() { - } - - /** - * Creates a new rulesets with the given string. The resulting rulesets will - * contain all referenced rulesets. - * - * @param rulesets - * the string with the rulesets to load - * @param factory - * the ruleset factory - * @return the rulesets - * @throws IllegalArgumentException - * if rulesets is empty (means, no rules have been found) or if - * a ruleset couldn't be found. - * @deprecated Internal API - */ - @InternalApi - @Deprecated - public static RuleSets getRuleSets(String rulesets, RuleSetFactory factory) { - RuleSets ruleSets = null; - try { - ruleSets = factory.createRuleSets(rulesets); - printRuleNamesInDebug(ruleSets); - if (ruleSets.ruleCount() == 0) { - String msg = "No rules found. Maybe you misspelled a rule name? (" + rulesets + ')'; - LOG.log(Level.SEVERE, msg); - throw new IllegalArgumentException(msg); - } - } catch (RuleSetNotFoundException rsnfe) { - LOG.log(Level.SEVERE, "Ruleset not found", rsnfe); - throw new IllegalArgumentException(rsnfe); - } - return ruleSets; - } - - /** - * See {@link #getRuleSets(String, RuleSetFactory)}. In addition, the - * loading of the rules is benchmarked. - * - * @param rulesets - * the string with the rulesets to load - * @param factory - * the ruleset factory - * @return the rulesets - * @throws IllegalArgumentException - * if rulesets is empty (means, no rules have been found) or if - * a ruleset couldn't be found. - * @deprecated Is internal API - */ - @InternalApi - @Deprecated - public static RuleSets getRuleSetsWithBenchmark(String rulesets, RuleSetFactory factory) { - try (TimedOperation to = TimeTracker.startOperation(TimedOperationCategory.LOAD_RULES)) { - return getRuleSets(rulesets, factory); - } - } - - /** - * If in debug modus, print the names of the rules. - * - * @param rulesets the RuleSets to print - */ - private static void printRuleNamesInDebug(RuleSets rulesets) { - if (LOG.isLoggable(Level.FINER)) { - for (Rule r : rulesets.getAllRules()) { - LOG.finer("Loaded rule " + r.getName()); - } - } - } -} 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 c59ccd52e1..fed369ad43 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,8 +29,6 @@ import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetLoader; -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; @@ -114,9 +112,12 @@ public class PMDTaskImpl { configuration.setRuleSets(project.replaceProperties(ruleSetString)); } - final RuleSets ruleSets = RulesetsFactoryUtils.getRuleSets(configuration.getRuleSets(), rulesetLoader.toFactory()); - List rulesetList = Arrays.asList(ruleSets.getAllRuleSets()); - logRulesUsed(ruleSets); + List rulesets = Arrays.asList(configuration.getRuleSets().split(",")); + List rulesetList = rulesetLoader.loadFromResources(rulesets); + if (rulesetList.isEmpty()) { + throw new BuildException("No rulesets"); + } + logRulesUsed(rulesetList); if (configuration.getSuppressMarker() != null) { project.log("Setting suppress marker to be " + configuration.getSuppressMarker(), Project.MSG_VERBOSE); @@ -294,10 +295,10 @@ public class PMDTaskImpl { } } - private void logRulesUsed(RuleSets rules) { + private void logRulesUsed(List rulesets) { project.log("Using these rulesets: " + configuration.getRuleSets(), Project.MSG_VERBOSE); - for (RuleSet ruleSet : rules.getAllRuleSets()) { + for (RuleSet ruleSet : rulesets) { for (Rule rule : ruleSet.getRules()) { project.log("Using rule " + rule.getName(), Project.MSG_VERBOSE); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java index 8a01f07572..854dab11d5 100755 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertyDescriptorField.java @@ -6,15 +6,12 @@ package net.sourceforge.pmd.properties; import java.util.Objects; -import net.sourceforge.pmd.RuleSetFactory; - /** * Field names for parsing the properties out of the ruleset xml files. These are intended to be used as the keys to a * map of fields to values. Most property descriptors can be built directly from such a map using their factory. * * @author Brian Remedios - * @see RuleSetFactory * @see PropertyTypeId * @deprecated Will be removed with 7.0.0 */ diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetSchemaTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetSchemaTest.java index aeb18f1369..a07b476176 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetSchemaTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetSchemaTest.java @@ -106,7 +106,7 @@ public class RuleSetSchemaTest { } public static class PMDRuleSetEntityResolver implements EntityResolver { - private static URL schema2 = RuleSetFactory.class.getResource("/ruleset_2_0_0.xsd"); + private static URL schema2 = PMDRuleSetEntityResolver.class.getResource("/ruleset_2_0_0.xsd"); private static SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); @Override 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 23de5ce3de..9712174595 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 @@ -8,15 +8,13 @@ import org.junit.Assert; import org.junit.Test; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetLoader; public class PMD5RulesetTest { @Test - public void loadRuleset() throws Exception { - RuleSetFactory ruleSetFactory = new RuleSetLoader().toFactory(); - RuleSet ruleset = ruleSetFactory.createRuleSet("net/sourceforge/pmd/lang/java/pmd5ruleset.xml"); + public void loadRuleset() { + RuleSet ruleset = new RuleSetLoader().loadFromResource("net/sourceforge/pmd/lang/java/pmd5ruleset.xml"); Assert.assertNotNull(ruleset); Assert.assertNull(ruleset.getRuleByName("GuardLogStatementJavaUtil")); Assert.assertNull(ruleset.getRuleByName("GuardLogStatement")); 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 16c3142ab9..c72a41b24e 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 @@ -15,9 +15,7 @@ import org.junit.Test; 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; public class QuickstartRulesetTest { @@ -26,15 +24,15 @@ public class QuickstartRulesetTest { @After public void cleanup() { - Handler[] handlers = Logger.getLogger(RuleSetFactory.class.getName()).getHandlers(); + Handler[] handlers = Logger.getLogger(RuleSetLoader.class.getName()).getHandlers(); for (Handler handler : handlers) { - Logger.getLogger(RuleSetFactory.class.getName()).removeHandler(handler); + Logger.getLogger(RuleSetLoader.class.getName()).removeHandler(handler); } } @Test - public void noDeprecations() throws RuleSetNotFoundException { - Logger.getLogger(RuleSetFactory.class.getName()).addHandler(new Handler() { + public void noDeprecations() { + Logger.getLogger(RuleSetLoader.class.getName()).addHandler(new Handler() { @Override public void publish(LogRecord record) { Assert.fail("No Logging expected: " + record.getMessage()); @@ -49,8 +47,8 @@ public class QuickstartRulesetTest { } }); - RuleSetFactory ruleSetFactory = new RuleSetLoader().enableCompatibility(false).toFactory(); - RuleSet quickstart = ruleSetFactory.createRuleSet("rulesets/java/quickstart.xml"); + RuleSetLoader ruleSetFactory = new RuleSetLoader().enableCompatibility(false); + RuleSet quickstart = ruleSetFactory.loadFromResource("rulesets/java/quickstart.xml"); Assert.assertFalse(quickstart.getRules().isEmpty()); } } From 3cf619e8e47ee736ba3d2bdba503874882098acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 19:15:19 +0100 Subject: [PATCH 287/299] Delete RulesetNotFoundException --- .../net/sourceforge/pmd/RuleSetFactory.java | 75 ++++--------------- .../pmd/RuleSetFactoryCompatibility.java | 8 +- .../sourceforge/pmd/RuleSetLoadException.java | 3 - .../net/sourceforge/pmd/RuleSetLoader.java | 14 ++-- .../pmd/RuleSetNotFoundException.java | 23 ------ .../sourceforge/pmd/RuleSetReferenceId.java | 17 ++--- .../sourceforge/pmd/util/ResourceLoader.java | 25 +++---- .../sourceforge/pmd/RuleSetFactoryTest.java | 2 +- 8 files changed, 39 insertions(+), 128 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java 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 3fca157b12..e3dbb3e702 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactory.java @@ -77,52 +77,16 @@ final class RuleSetFactory { * @param ruleSetReferenceId * The RuleSetReferenceId of the RuleSet to create. * @return A new RuleSet. - * @throws RuleSetNotFoundException - * if unable to find a resource. */ - RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException { + RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) { return createRuleSet(ruleSetReferenceId, includeDeprecatedRuleReferences); } private RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) - throws RuleSetNotFoundException { + throws RuleSetLoadException { return parseRuleSetNode(ruleSetReferenceId, withDeprecatedRuleReferences); } - /** - * 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 - * - * @deprecated Use {@link RuleSet#copy(RuleSet)} - */ - @Deprecated - public RuleSet createRuleSetCopy(RuleSet original) { - RuleSetBuilder builder = new RuleSetBuilder(original); - return builder.build(); - } - - /** - * 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. @@ -137,14 +101,11 @@ final class RuleSetFactory { * Whether RuleReferences that are deprecated should be ignored * or not * @return A new Rule. - * @throws RuleSetNotFoundException - * if unable to find a resource. */ - private Rule createRule(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) - throws RuleSetNotFoundException { + private Rule createRule(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) { if (ruleSetReferenceId.isAllRules()) { throw new IllegalArgumentException( - "Cannot parse a single Rule from an all Rule RuleSet reference: <" + ruleSetReferenceId + ">."); + "Cannot parse a single Rule from an all Rule RuleSet reference: <" + ruleSetReferenceId + ">."); } RuleSet ruleSet; // java8: computeIfAbsent @@ -160,20 +121,18 @@ final class RuleSetFactory { /** * Parse a ruleset node to construct a RuleSet. * - * @param ruleSetReferenceId - * The RuleSetReferenceId of the RuleSet being parsed. - * @param withDeprecatedRuleReferences - * whether rule references that are deprecated should be ignored - * or not + * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being parsed. + * @param withDeprecatedRuleReferences whether rule references that are deprecated should be ignored + * or not + * * @return The new RuleSet. */ - private RuleSet parseRuleSetNode(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) - throws RuleSetNotFoundException { + private RuleSet parseRuleSetNode(RuleSetReferenceId ruleSetReferenceId, boolean withDeprecatedRuleReferences) { try (CheckedInputStream inputStream = new CheckedInputStream( - ruleSetReferenceId.getInputStream(resourceLoader), new Adler32());) { + ruleSetReferenceId.getInputStream(resourceLoader), new Adler32());) { if (!ruleSetReferenceId.isExternal()) { throw new IllegalArgumentException( - "Cannot parse a RuleSet from a non-external reference: <" + ruleSetReferenceId + ">."); + "Cannot parse a RuleSet from a non-external reference: <" + ruleSetReferenceId + ">."); } DocumentBuilder builder = createDocumentBuilder(); InputSource inputSource = new InputSource(inputStream); @@ -299,8 +258,7 @@ final class RuleSetFactory { * @param rulesetReferences keeps track of already processed complete ruleset references in order to log a warning */ private void parseRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, Node ruleNode, - boolean withDeprecatedRuleReferences, Set rulesetReferences) - throws RuleSetNotFoundException { + boolean withDeprecatedRuleReferences, Set rulesetReferences) { Element ruleElement = (Element) ruleNode; String ref = ruleElement.getAttribute("ref"); ref = compatibilityFilter.applyRef(ref, this.warnDeprecated); @@ -330,8 +288,7 @@ final class RuleSetFactory { * The RuleSet reference. * @param rulesetReferences keeps track of already processed complete ruleset references in order to log a warning */ - private void parseRuleSetReferenceNode(RuleSetBuilder ruleSetBuilder, Element ruleElement, String ref, Set rulesetReferences) - throws RuleSetNotFoundException { + private void parseRuleSetReferenceNode(RuleSetBuilder ruleSetBuilder, Element ruleElement, String ref, Set rulesetReferences) { String priority = null; NodeList childNodes = ruleElement.getChildNodes(); Set excludedRulesCheck = new HashSet<>(); @@ -455,13 +412,13 @@ final class RuleSetFactory { * or not */ private void parseRuleReferenceNode(RuleSetReferenceId ruleSetReferenceId, RuleSetBuilder ruleSetBuilder, - Node ruleNode, String ref, boolean withDeprecatedRuleReferences) throws RuleSetNotFoundException { + Node ruleNode, String ref, boolean withDeprecatedRuleReferences) { Element ruleElement = (Element) ruleNode; // Stop if we're looking for a particular Rule, and this element is not // it. if (StringUtils.isNotBlank(ruleSetReferenceId.getRuleName()) - && !isRuleName(ruleElement, ruleSetReferenceId.getRuleName())) { + && !isRuleName(ruleElement, ruleSetReferenceId.getRuleName())) { return; } @@ -565,7 +522,7 @@ final class RuleSetFactory { } } } catch (Exception e) { - throw new RuntimeException(e); + throw new RuleSetLoadException("Cannot load " + ruleSetReferenceId, e); } return found; 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 c063157b83..d730065b47 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetFactoryCompatibility.java @@ -22,6 +22,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; final class RuleSetFactoryCompatibility { static final RuleSetFactoryCompatibility EMPTY = new RuleSetFactoryCompatibility(); + /** The instance with the built-in filters for the modified PMD rules. */ static final RuleSetFactoryCompatibility DEFAULT = new RuleSetFactoryCompatibility(); @@ -79,13 +80,6 @@ final class RuleSetFactoryCompatibility { private final List filters = new ArrayList<>(); - /** - * Creates a new instance of the compatibility filter with the built-in - * filters for the modified PMD rules. - */ - RuleSetFactoryCompatibility() { - - } void addFilterRuleMovedAndRenamed(String language, String oldRuleset, String oldName, String newRuleset, String newName) { filters.add(RuleSetFilter.ruleMoved(language, oldRuleset, newRuleset, oldName)); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java index a59321e146..4fa14bcb62 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoadException.java @@ -11,9 +11,6 @@ import net.sourceforge.pmd.annotation.InternalApi; * {@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 { 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 af8f96be27..1aee4342b5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetLoader.java @@ -9,10 +9,10 @@ import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; 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 org.checkerframework.checker.nullness.qual.NonNull; @@ -29,8 +29,6 @@ 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; @@ -220,7 +218,7 @@ public final class RuleSetLoader { */ public List getStandardRuleSets() { String rulesetsProperties; - List ruleSetReferenceIds = new ArrayList<>(); + List ruleSetReferenceIds = new ArrayList<>(); for (Language language : LanguageRegistry.getLanguages()) { Properties props = new Properties(); rulesetsProperties = "category/" + language.getTerseName() + "/categories.properties"; @@ -228,11 +226,9 @@ public final class RuleSetLoader { props.load(inputStream); String rulesetFilenames = props.getProperty("rulesets.filenames"); if (rulesetFilenames != null) { - ruleSetReferenceIds.addAll(RuleSetReferenceId.parse(rulesetFilenames)); + ruleSetReferenceIds.addAll(Arrays.asList(rulesetFilenames.split(","))); } - } catch (RuleSetNotFoundException e) { - LOG.warning("The language " + language.getTerseName() + " provides no " + rulesetsProperties + "."); - } catch (IOException ioe) { + } catch (IOException e) { 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")); @@ -240,7 +236,7 @@ public final class RuleSetLoader { } List ruleSets = new ArrayList<>(); - for (RuleSetReferenceId id : ruleSetReferenceIds) { + for (String 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 deleted file mode 100644 index 4e3acad5d4..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetNotFoundException.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -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) { - super(msg); - } - - public RuleSetNotFoundException(String msg, Throwable cause) { - super(msg, cause); - } -} 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 66dd5e47c9..3f9b2e1963 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; @@ -397,19 +398,15 @@ public class RuleSetReferenceId { * * @param rl The {@link ResourceLoader} to use. * @return An InputStream to that resource. - * @throws RuleSetNotFoundException - * if unable to find a resource. */ - public InputStream getInputStream(final ResourceLoader rl) throws RuleSetNotFoundException { + public InputStream getInputStream(final ResourceLoader rl) throws IOException { if (externalRuleSetReferenceId == null) { - InputStream in = StringUtils.isBlank(ruleSetFileName) ? null - : rl.loadResourceAsStream(ruleSetFileName); - if (in == null) { - throw new RuleSetNotFoundException("Can't find resource '" + ruleSetFileName + "' for rule '" + ruleName - + "'" + ". Make sure the resource is a valid file or URL and is on the CLASSPATH. " - + "Here's the current classpath: " + System.getProperty("java.class.path")); + if (StringUtils.isBlank(ruleSetFileName)) { + throw new FileNotFoundException("Can't find resource '" + ruleSetFileName + "' for rule '" + ruleName + + "'" + ". Make sure the resource is a valid file or URL and is on the CLASSPATH. " + + "Here's the current classpath: " + System.getProperty("java.class.path")); } - return in; + return rl.loadResourceAsStream(ruleSetFileName); } else { return externalRuleSetReferenceId.getInputStream(rl); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java index 5f9fe917bc..68c2776da5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.util; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; @@ -14,7 +15,6 @@ import java.nio.file.Files; import java.util.Objects; import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.RuleSetNotFoundException; import net.sourceforge.pmd.annotation.InternalApi; /** @@ -58,10 +58,10 @@ public class ResourceLoader { * Caller is responsible for closing the {@link InputStream}. * * @param name The resource to attempt and load + * * @return InputStream - * @throws RuleSetNotFoundException */ - public InputStream loadResourceAsStream(final String name) throws RuleSetNotFoundException { + public InputStream loadResourceAsStream(final String name) throws IOException { // Search file locations first final File file = new File(name); if (file.exists()) { @@ -69,7 +69,8 @@ public class ResourceLoader { return Files.newInputStream(file.toPath()); } catch (final IOException e) { // if the file didn't exist, we wouldn't be here - throw new RuntimeException(e); // somehow the file vanished between checking for existence and opening + // somehow the file vanished between checking for existence and opening + throw new IOException("File was checked to exist", e); } } @@ -82,13 +83,11 @@ public class ResourceLoader { } catch (final Exception e) { try { return loadClassPathResourceAsStream(name); - } catch (final IOException ignored) { - // We will throw our own exception, with a different message + } catch (final IOException ioe) { + throw new IOException("Can't find resource " + name + ". Make sure the resource is a valid file or URL or is on the CLASSPATH", ioe); } } - throw new RuleSetNotFoundException("Can't find resource " + name - + ". Make sure the resource is a valid file or URL or is on the CLASSPATH"); } public InputStream loadClassPathResourceAsStream(final String name) throws IOException { @@ -110,7 +109,7 @@ public class ResourceLoader { } } - public InputStream loadClassPathResourceAsStreamOrThrow(final String name) throws RuleSetNotFoundException { + public InputStream loadClassPathResourceAsStreamOrThrow(final String name) throws IOException { InputStream is = null; try { is = loadClassPathResourceAsStream(name); @@ -119,7 +118,7 @@ public class ResourceLoader { } if (is == null) { - throw new RuleSetNotFoundException("Can't find resource " + name + throw new FileNotFoundException("Can't find resource " + name + ". Make sure the resource is on the CLASSPATH"); } @@ -128,12 +127,6 @@ public class ResourceLoader { /** * Load the rule from the classloader from resource loader, consistent with the ruleset - * - * @param clazz - * @return - * @throws ClassNotFoundException - * @throws IllegalAccessException - * @throws InstantiationException */ public Rule loadRuleFromClassPath(final String clazz) throws ClassNotFoundException, IllegalAccessException, InstantiationException { return (Rule) classLoader.loadClass(clazz).newInstance(); 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 d405f99cb4..0b0bab592b 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetFactoryTest.java @@ -579,7 +579,7 @@ public class RuleSetFactoryTest { } @Test - public void testIncorrectExternalRef() { + public void testIncorrectExternalRef() { assertCannotParse(REF_MISSPELLED_XREF); } From 789e4aa73004fc4e25ebf0bc2db8a3d89d510d10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 19:31:00 +0100 Subject: [PATCH 288/299] Fix tests --- .../pmd/lang/apex/DefaultRulesetTest.java | 7 +++--- .../main/java/net/sourceforge/pmd/PMD.java | 3 ++- .../sourceforge/pmd/RuleSetReferenceId.java | 17 ++++++++++---- .../sourceforge/pmd/util/ResourceLoader.java | 23 +++++++++++-------- .../pmd/docs/RuleDocGeneratorTest.java | 2 +- .../pmd/docs/RuleSetResolverTest.java | 13 +++++------ .../java/net/sourceforge/pmd/cli/CLITest.java | 13 +++++------ .../pmd/AbstractLanguageVersionTest.java | 7 ++++-- .../pmd/AbstractRuleSetFactoryTest.java | 3 +++ 9 files changed, 52 insertions(+), 36 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 45850e4649..617dd1bd78 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 @@ -15,7 +15,6 @@ import org.junit.Test; import org.junit.contrib.java.lang.system.SystemErrRule; import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSetFactory; import net.sourceforge.pmd.RuleSetLoader; public class DefaultRulesetTest { @@ -30,15 +29,15 @@ public class DefaultRulesetTest { @After public void cleanup() { - Handler[] handlers = Logger.getLogger(RuleSetFactory.class.getName()).getHandlers(); + Handler[] handlers = Logger.getLogger(RuleSetLoader.class.getName()).getHandlers(); for (Handler handler : handlers) { - Logger.getLogger(RuleSetFactory.class.getName()).removeHandler(handler); + Logger.getLogger(RuleSetLoader.class.getName()).removeHandler(handler); } } @Test public void loadQuickstartRuleset() { - Logger.getLogger(RuleSetFactory.class.getName()).addHandler(new Handler() { + Logger.getLogger(RuleSetLoader.class.getName()).addHandler(new Handler() { @Override public void publish(LogRecord record) { Assert.fail("No Logging expected: " + record.getMessage()); 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 e514ea24cc..7c73b3f194 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -278,7 +278,8 @@ public class PMD { ruleSets = factory.loadFromResources(rulesetPaths); printRuleNamesInDebug(ruleSets); if (isEmpty(ruleSets)) { - String msg = "No rules found. Maybe you misspelled a rule name? (" + rulesetPaths + ')'; + String msg = "No rules found. Maybe you misspelled a rule name? (" + + String.join(",", rulesetPaths) + ')'; LOG.log(Level.SEVERE, msg); throw new IllegalArgumentException(msg); } 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 3f9b2e1963..d7ade89948 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetReferenceId.java @@ -402,16 +402,25 @@ public class RuleSetReferenceId { public InputStream getInputStream(final ResourceLoader rl) throws IOException { if (externalRuleSetReferenceId == null) { if (StringUtils.isBlank(ruleSetFileName)) { - throw new FileNotFoundException("Can't find resource '" + ruleSetFileName + "' for rule '" + ruleName - + "'" + ". Make sure the resource is a valid file or URL and is on the CLASSPATH. " - + "Here's the current classpath: " + System.getProperty("java.class.path")); + throw notFoundException(); + } + try { + return rl.loadResourceAsStream(ruleSetFileName); + } catch (FileNotFoundException ignored) { + throw notFoundException(); } - return rl.loadResourceAsStream(ruleSetFileName); } else { return externalRuleSetReferenceId.getInputStream(rl); } } + private FileNotFoundException notFoundException() { + return new FileNotFoundException("Can't find resource '" + ruleSetFileName + "' for rule '" + ruleName + + "'" + ". Make sure the resource is a valid file or URL and is on the classpath. " + + "Here's the current classpath: " + + System.getProperty("java.class.path")); + } + /** * Return the String form of this Rule reference. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java index 68c2776da5..d9d4d7e5b0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/ResourceLoader.java @@ -14,6 +14,9 @@ import java.net.URLConnection; import java.nio.file.Files; import java.util.Objects; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.annotation.InternalApi; @@ -61,7 +64,7 @@ public class ResourceLoader { * * @return InputStream */ - public InputStream loadResourceAsStream(final String name) throws IOException { + public @NonNull InputStream loadResourceAsStream(final String name) throws IOException { // Search file locations first final File file = new File(name); if (file.exists()) { @@ -79,18 +82,18 @@ public class ResourceLoader { final HttpURLConnection connection = (HttpURLConnection) new URL(name).openConnection(); connection.setConnectTimeout(TIMEOUT); connection.setReadTimeout(TIMEOUT); - return connection.getInputStream(); - } catch (final Exception e) { - try { - return loadClassPathResourceAsStream(name); - } catch (final IOException ioe) { - throw new IOException("Can't find resource " + name + ". Make sure the resource is a valid file or URL or is on the CLASSPATH", ioe); + InputStream is = connection.getInputStream(); + if (is != null) { + return is; } + } catch (final Exception e) { + return loadClassPathResourceAsStreamOrThrow(name); } + throw new IOException("Can't find resource " + name + ". Make sure the resource is a valid file or URL or is on the classpath"); } - public InputStream loadClassPathResourceAsStream(final String name) throws IOException { + public @Nullable InputStream loadClassPathResourceAsStream(final String name) throws IOException { /* * Don't use getResourceAsStream to avoid reusing connections between threads * See https://github.com/pmd/pmd/issues/234 @@ -109,7 +112,7 @@ public class ResourceLoader { } } - public InputStream loadClassPathResourceAsStreamOrThrow(final String name) throws IOException { + public @NonNull InputStream loadClassPathResourceAsStreamOrThrow(final String name) throws IOException { InputStream is = null; try { is = loadClassPathResourceAsStream(name); @@ -119,7 +122,7 @@ public class ResourceLoader { if (is == null) { throw new FileNotFoundException("Can't find resource " + name - + ". Make sure the resource is on the CLASSPATH"); + + ". Make sure the resource is on the classpath"); } return is; 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 7d087032ae..23114c6e53 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 @@ -59,7 +59,7 @@ public class RuleDocGeneratorTest { public void testSingleRuleset() throws IOException { RuleDocGenerator generator = new RuleDocGenerator(writer, root); - RuleSetLoader rsf = new RuleSetLoader(); + RuleSetLoader rsf = new RuleSetLoader().includeDeprecatedRuleReferences(true); RuleSet ruleset = rsf.loadFromResource("rulesets/ruledoctest/sample.xml"); generator.generate(Arrays.asList(ruleset), diff --git a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java index f5bc92714c..8b82683953 100644 --- a/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java +++ b/pmd-doc/src/test/java/net/sourceforge/pmd/docs/RuleSetResolverTest.java @@ -4,9 +4,10 @@ package net.sourceforge.pmd.docs; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; + import java.nio.file.FileSystems; import java.nio.file.Path; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -17,11 +18,9 @@ import net.sourceforge.pmd.RuleSetLoader; public class RuleSetResolverTest { - private static final List excludedRulesets = new ArrayList<>(); - - static { - excludedRulesets.add(FilenameUtils.normalize("pmd-test/src/main/resources/rulesets/dummy/basic.xml")); - } + private static final List EXCLUDED_RULESETS = listOf( + FilenameUtils.normalize("pmd-test/src/main/resources/rulesets/dummy/basic.xml") + ); @Test public void resolveAllRulesets() { @@ -39,7 +38,7 @@ public class RuleSetResolverTest { Iterator it = additionalRulesets.iterator(); while (it.hasNext()) { String filename = it.next(); - for (String exclusion : excludedRulesets) { + for (String exclusion : EXCLUDED_RULESETS) { if (filename.endsWith(exclusion)) { it.remove(); break; diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java b/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java index 3e82902345..2fc217381d 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/cli/CLITest.java @@ -7,7 +7,6 @@ package net.sourceforge.pmd.cli; import static org.junit.Assert.assertTrue; import java.io.File; -import java.io.IOException; import java.util.regex.Pattern; import org.junit.Assert; @@ -39,7 +38,7 @@ public class CLITest extends BaseCLITest { } @Test - public void changeJavaVersion() throws IOException { + public void changeJavaVersion() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml", "-version", "1.5", "-language", "java", "-debug", }; String resultFilename = runTest(args, "chgJavaVersion"); @@ -54,14 +53,14 @@ public class CLITest extends BaseCLITest { } @Test - public void exitStatusWithViolations() throws IOException { + public void exitStatusWithViolations() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/errorprone.xml", }; String resultFilename = runTest(args, "exitStatusWithViolations", 4); assertTrue(FileUtil.findPatternInFile(new File(resultFilename), "Avoid empty if")); } @Test - public void exitStatusWithViolationsAndWithoutFailOnViolations() throws IOException { + public void exitStatusWithViolationsAndWithoutFailOnViolations() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/errorprone.xml", "-failOnViolation", "false", }; String resultFilename = runTest(args, "exitStatusWithViolationsAndWithoutFailOnViolations", 0); assertTrue(FileUtil.findPatternInFile(new File(resultFilename), "Avoid empty if")); @@ -71,7 +70,7 @@ public class CLITest extends BaseCLITest { * See https://sourceforge.net/p/pmd/bugs/1231/ */ @Test - public void testWrongRuleset() throws Exception { + public void testWrongRuleset() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/designn.xml", }; String filename = TEST_OUPUT_DIRECTORY + "testWrongRuleset.txt"; createTestOutputFile(filename); @@ -85,7 +84,7 @@ public class CLITest extends BaseCLITest { * See https://sourceforge.net/p/pmd/bugs/1231/ */ @Test - public void testWrongRulesetWithRulename() throws Exception { + public void testWrongRulesetWithRulename() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/designn.xml/UseCollectionIsEmpty", }; String filename = TEST_OUPUT_DIRECTORY + "testWrongRuleset.txt"; createTestOutputFile(filename); @@ -99,7 +98,7 @@ public class CLITest extends BaseCLITest { * See https://sourceforge.net/p/pmd/bugs/1231/ */ @Test - public void testWrongRulename() throws Exception { + public void testWrongRulename() { String[] args = { "-d", SOURCE_FOLDER, "-f", "text", "-R", "category/java/design.xml/ThisRuleDoesNotExist", }; String filename = TEST_OUPUT_DIRECTORY + "testWrongRuleset.txt"; createTestOutputFile(filename); diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java index 8834636264..41a01c7833 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractLanguageVersionTest.java @@ -101,8 +101,11 @@ public class AbstractLanguageVersionTest { } Properties props = new Properties(); - String rulesetsProperties = "category/" + simpleTerseName + "/categories.properties"; + String rulesetsProperties = "/category/" + simpleTerseName + "/categories.properties"; try (InputStream inputStream = getClass().getResourceAsStream(rulesetsProperties)) { + if (inputStream == null) { + throw new IOException(); + } props.load(inputStream); } assertRulesetsAndCategoriesProperties(props); @@ -122,7 +125,7 @@ public class AbstractLanguageVersionTest { } Properties props = new Properties(); - String rulesetsProperties = "rulesets/" + simpleTerseName + "/rulesets.properties"; + String rulesetsProperties = "/rulesets/" + simpleTerseName + "/rulesets.properties"; InputStream inputStream = getClass().getResourceAsStream(rulesetsProperties); if (inputStream != null) { // rulesets.properties file exists 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 1e5761a3cf..b262bbcf46 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/AbstractRuleSetFactoryTest.java @@ -18,6 +18,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -285,11 +286,13 @@ public abstract class AbstractRuleSetFactoryTest { private List getRuleSetFileNames(String language) throws IOException { List ruleSetFileNames = new ArrayList<>(); Properties properties = new Properties(); + @SuppressWarnings("PMD.CloseResource") InputStream input = getClass().getResourceAsStream("rulesets/" + language + "/rulesets.properties"); if (input == null) { // this might happen if a language is only support by CPD, but not // by PMD System.err.println("No ruleset found for language " + language); + return Collections.emptyList(); } try (InputStream is = input) { properties.load(is); From 86ea3a0fb7dba43965e906a702aceaaa53e24d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 20:31:50 +0100 Subject: [PATCH 289/299] Use list of ruleset in configuration --- .../main/java/net/sourceforge/pmd/PMD.java | 8 +--- .../net/sourceforge/pmd/PMDConfiguration.java | 39 ++++++++++++++++--- .../pmd/ant/internal/PMDTaskImpl.java | 14 +++---- .../sourceforge/pmd/cli/PMDParameters.java | 3 +- .../sourceforge/pmd/ConfigurationTest.java | 7 ---- 5 files changed, 44 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 7c73b3f194..068afe906f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -220,11 +220,7 @@ public class PMD { // Load the RuleSets final RuleSetLoader ruleSetFactory = RuleSetLoader.fromPmdConfig(configuration); - List rulesetPaths = Arrays.asList(configuration.getRuleSets().split(",")); - final RuleSets ruleSets = new RuleSets(getRuleSetsWithBenchmark(rulesetPaths, ruleSetFactory)); - if (ruleSets == null) { - return PMDCommandLineInterface.NO_ERRORS_STATUS; - } + final RuleSets ruleSets = new RuleSets(getRuleSetsWithBenchmark(configuration.getRuleSetPaths(), ruleSetFactory)); final Set languages = getApplicableLanguages(configuration, ruleSets); final List files = getApplicableFiles(configuration, languages); @@ -273,7 +269,7 @@ public class PMD { private static List getRuleSetsWithBenchmark(List rulesetPaths, RuleSetLoader factory) { try (TimedOperation to = TimeTracker.startOperation(TimedOperationCategory.LOAD_RULES)) { - List ruleSets = null; + List ruleSets; try { ruleSets = factory.loadFromResources(rulesetPaths); printRuleNamesInDebug(ruleSets); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java index 1c77fec7fc..79dafd3485 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -6,10 +6,14 @@ package net.sourceforge.pmd; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Properties; +import org.checkerframework.checker.nullness.qual.NonNull; + +import net.sourceforge.pmd.annotation.DeprecatedUntil700; import net.sourceforge.pmd.cache.AnalysisCache; import net.sourceforge.pmd.cache.FileAnalysisCache; import net.sourceforge.pmd.cache.NoopAnalysisCache; @@ -44,7 +48,7 @@ import net.sourceforge.pmd.util.ClasspathClassLoader; * *

    The aspects related to Rules and Source files are:

    *
      - *
    • A comma separated list of RuleSets URIs. {@link #getRuleSets()}
    • + *
    • RuleSets URIs: {@link #getRuleSetPaths()}
    • *
    • A minimum priority threshold when loading Rules from RuleSets, defaults * to {@link RulePriority#LOW}. {@link #getMinimumPriority()}
    • *
    • The character encoding of source files, defaults to the system default as @@ -86,7 +90,7 @@ public class PMDConfiguration extends AbstractConfiguration { private LanguageVersionDiscoverer languageVersionDiscoverer = new LanguageVersionDiscoverer(); // Rule and source file options - private String ruleSets; + private List ruleSets; private RulePriority minimumPriority = RulePriority.LOW; private String inputPaths; private String inputUri; @@ -258,19 +262,44 @@ public class PMDConfiguration extends AbstractConfiguration { * Get the comma separated list of RuleSet URIs. * * @return The RuleSet URIs. + * + * @deprecated Use {@link #getRuleSetPaths()} */ + @Deprecated + @DeprecatedUntil700 public String getRuleSets() { + return String.join(",", ruleSets); + } + + /** + * Returns the list of ruleset URIs. + * + * @see RuleSetLoader#loadFromResource(String) + */ + public List getRuleSetPaths() { return ruleSets; } + /** + * Sets the rulesets. + * + * @throws NullPointerException If the parameter is null + */ + public void setRuleSets(@NonNull List ruleSets) { + this.ruleSets = new ArrayList<>(ruleSets); + } + /** * Set the comma separated list of RuleSet URIs. * - * @param ruleSets - * the rulesets to set + * @param ruleSets the rulesets to set + * + * @deprecated Use {@link #setRuleSets(List)} */ + @Deprecated + @DeprecatedUntil700 public void setRuleSets(String ruleSets) { - this.ruleSets = ruleSets; + this.ruleSets = Arrays.asList(ruleSets.split(",")); } /** 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 fed369ad43..9fba58463c 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 @@ -51,6 +51,7 @@ public class PMDTaskImpl { private final List formatters = new ArrayList<>(); private final List filesets = new ArrayList<>(); private final PMDConfiguration configuration = new PMDConfiguration(); + private final String rulesetPaths; private boolean failOnError; private boolean failOnRuleViolation; private int maxRuleViolations = 0; @@ -68,7 +69,7 @@ public class PMDTaskImpl { if (this.maxRuleViolations > 0) { this.failOnRuleViolation = true; } - configuration.setRuleSets(task.getRulesetFiles()); + this.rulesetPaths = task.getRulesetFiles() == null ? "" : task.getRulesetFiles(); configuration.setRuleSetFactoryCompatibilityEnabled(!task.isNoRuleSetCompatibility()); if (task.getEncoding() != null) { configuration.setSourceEncoding(task.getEncoding()); @@ -106,13 +107,10 @@ public class PMDTaskImpl { .loadResourcesWith(setupResourceLoader()); // 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)); - } + // Substitute env variables/properties + String ruleSetString = project.replaceProperties(rulesetPaths); - List rulesets = Arrays.asList(configuration.getRuleSets().split(",")); + List rulesets = Arrays.asList(ruleSetString.split(",")); List rulesetList = rulesetLoader.loadFromResources(rulesets); if (rulesetList.isEmpty()) { throw new BuildException("No rulesets"); @@ -296,7 +294,7 @@ public class PMDTaskImpl { } private void logRulesUsed(List rulesets) { - project.log("Using these rulesets: " + configuration.getRuleSets(), Project.MSG_VERBOSE); + project.log("Using these rulesets: " + rulesetPaths, Project.MSG_VERBOSE); for (RuleSet ruleSet : rulesets) { for (Rule rule : ruleSet.getRules()) { 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 0f37b74d06..5b05337fac 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 @@ -6,6 +6,7 @@ package net.sourceforge.pmd.cli; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Properties; @@ -203,7 +204,7 @@ public class PMDParameters { configuration.setReportFile(this.getReportfile()); configuration.setReportProperties(this.getProperties()); configuration.setReportShortNames(this.isShortnames()); - configuration.setRuleSets(this.getRulesets()); + configuration.setRuleSets(Arrays.asList(this.getRulesets().split(","))); configuration.setRuleSetFactoryCompatibilityEnabled(!this.noRuleSetCompatibility); configuration.setShowSuppressedViolations(this.isShowsuppressed()); configuration.setSourceEncoding(this.getEncoding()); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/ConfigurationTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/ConfigurationTest.java index 480bdb24a1..d765920004 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/ConfigurationTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/ConfigurationTest.java @@ -111,13 +111,6 @@ public class ConfigurationTest { Assert.assertArrayEquals(expectedUris, uris); } - @Test - public void testRuleSets() { - PMDConfiguration configuration = new PMDConfiguration(); - assertEquals("Default RuleSets", null, configuration.getRuleSets()); - configuration.setRuleSets("/rulesets/basic.xml"); - assertEquals("Changed RuleSets", "/rulesets/basic.xml", configuration.getRuleSets()); - } @Test public void testMinimumPriority() { From 7d69a67ae6b328403cf77a0bffe52b1ead73e951 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 20:39:41 +0100 Subject: [PATCH 290/299] Remove deprecated methods in ruleset --- .../java/net/sourceforge/pmd/RuleSet.java | 24 ------------------- .../net/sourceforge/pmd/RuleSetWriter.java | 9 +++---- .../java/net/sourceforge/pmd/RuleSetTest.java | 24 +++++++++++-------- 3 files changed, 19 insertions(+), 38 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 06a3469b99..672a16b27d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSet.java @@ -553,18 +553,6 @@ public class RuleSet implements ChecksumAware { } } - /** - * @deprecated Use {@link #getFileExclusions()} - */ - @Deprecated - public List getExcludePatterns() { - List excludes = new ArrayList<>(); - for (Pattern p : excludePatterns) { - excludes.add(p.pattern()); - } - return excludes; - } - /** * Returns the number of rules in this ruleset * @@ -722,18 +710,6 @@ public class RuleSet implements ChecksumAware { return description; } - /** - * @deprecated Use {@link #getFileInclusions()} - */ - @Deprecated - public List getIncludePatterns() { - List includes = new ArrayList<>(); - for (Pattern p : includePatterns) { - includes.add(p.pattern()); - } - return includes; - } - /** * Returns the file exclusion patterns as an unmodifiable list. */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java index 4719748ad0..126d768b3a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleSetWriter.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.FactoryConfigurationError; @@ -104,12 +105,12 @@ public class RuleSetWriter { Element descriptionElement = createDescriptionElement(ruleSet.getDescription()); ruleSetElement.appendChild(descriptionElement); - for (String excludePattern : ruleSet.getExcludePatterns()) { - Element excludePatternElement = createExcludePatternElement(excludePattern); + for (Pattern excludePattern : ruleSet.getFileExclusions()) { + Element excludePatternElement = createExcludePatternElement(excludePattern.pattern()); ruleSetElement.appendChild(excludePatternElement); } - for (String includePattern : ruleSet.getIncludePatterns()) { - Element includePatternElement = createIncludePatternElement(includePattern); + for (Pattern includePattern : ruleSet.getFileInclusions()) { + Element includePatternElement = createIncludePatternElement(includePattern.pattern()); ruleSetElement.appendChild(includePatternElement); } for (Rule rule : ruleSet.getRules()) { 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 374baa6da5..5a67ff65d0 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.Random; import java.util.Set; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.commons.io.FilenameUtils; import org.checkerframework.checker.nullness.qual.NonNull; @@ -269,8 +270,8 @@ public class RuleSetTest { createRuleSetBuilder("ruleset1") .withFileExclusions(Pattern.compile(".*")) .build(); - assertNotNull("Exclude patterns", ruleSet.getExcludePatterns()); - assertEquals("Invalid number of patterns", 1, ruleSet.getExcludePatterns().size()); + assertNotNull("Exclude patterns", ruleSet.getFileExclusions()); + assertEquals("Invalid number of patterns", 1, ruleSet.getFileExclusions().size()); } @Test @@ -280,24 +281,28 @@ public class RuleSetTest { .withFileExclusions(Pattern.compile(".*")) .withFileExclusions(Pattern.compile(".*ha")) .build(); - assertEquals("Exclude pattern", Arrays.asList(".*", ".*ha"), ruleSet2.getExcludePatterns()); + assertEquals("Exclude pattern", Arrays.asList(".*", ".*ha"), toStrings(ruleSet2.getFileExclusions())); } @Test public void testIncludePatternsAreOrdered() { RuleSet ruleSet2 = createRuleSetBuilder("ruleset2") - .withFileInclusions(Pattern.compile(".*")) - .withFileInclusions(Arrays.asList(Pattern.compile(".*ha"), Pattern.compile(".*hb"))) - .build(); - assertEquals("Exclude pattern", Arrays.asList(".*", ".*ha", ".*hb"), ruleSet2.getIncludePatterns()); + .withFileInclusions(Pattern.compile(".*")) + .withFileInclusions(Arrays.asList(Pattern.compile(".*ha"), Pattern.compile(".*hb"))) + .build(); + assertEquals("Exclude pattern", Arrays.asList(".*", ".*ha", ".*hb"), toStrings(ruleSet2.getFileInclusions())); + } + + private List toStrings(List strings) { + return strings.stream().map(Pattern::pattern).collect(Collectors.toList()); } @Test public void testAddExcludePatterns() { RuleSet ruleSet = createRuleSetBuilder("ruleset1") - .withFileExclusions(Pattern.compile(".*")) - .build(); + .withFileExclusions(Pattern.compile(".*")) + .build(); assertNotNull("Exclude patterns", ruleSet.getFileExclusions()); assertEquals("Invalid number of patterns", 1, ruleSet.getFileExclusions().size()); @@ -315,7 +320,6 @@ public class RuleSetTest { excludePatterns.add(Pattern.compile("ah*")); excludePatterns.add(Pattern.compile(".*")); RuleSet ruleSet = createRuleSetBuilder("ruleset").replaceFileExclusions(excludePatterns).build(); - assertNotNull("Exclude patterns", ruleSet.getExcludePatterns()); assertNotNull("Exclude patterns", ruleSet.getFileExclusions()); assertEquals("Invalid number of exclude patterns", 2, ruleSet.getFileExclusions().size()); assertEquals("Exclude pattern", "ah*", ruleSet.getFileExclusions().get(0).pattern()); From 79def1b51d8bee8c6d75df44270b06264090bf72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 21:13:12 +0100 Subject: [PATCH 291/299] Reduce importance of parser options --- .../pmd/lang/apex/ApexHandler.java | 5 - .../pmd/lang/apex/ApexParserOptions.java | 18 -- .../pmd/lang/apex/rule/AbstractApexRule.java | 7 - .../main/java/net/sourceforge/pmd/PMD.java | 29 --- .../sourceforge/pmd/SourceCodeProcessor.java | 5 +- .../AbstractPmdLanguageVersionHandler.java | 47 ++++ .../pmd/lang/LanguageVersionHandler.java | 5 + .../sourceforge/pmd/lang/ParserOptions.java | 53 +---- .../net/sourceforge/pmd/lang/ast/Parser.java | 55 ++++- .../net/sourceforge/pmd/ExcludeLinesTest.java | 6 +- .../lang/ecmascript/EcmascriptHandler.java | 2 +- .../lang/ecmascript/ast/EcmascriptParser.java | 6 +- .../ecmascript/ast/EcmascriptParserTest.java | 7 +- .../pmd/lang/ast/test/BaseParsingHelper.kt | 19 +- .../pmd/lang/ParserOptionsTest.java | 19 -- .../pmd/lang/ParserOptionsUnitTest.java | 206 ------------------ .../sourceforge/pmd/lang/vf/VfHandler.java | 40 +++- .../pmd/lang/vf/VfParserOptions.java | 49 ----- .../lang/vf/ast/VfExpressionTypeVisitor.java | 11 +- .../sourceforge/pmd/lang/vf/ast/VfParser.java | 1 - .../pmd/lang/vf/VfParserOptionsTest.java | 22 -- .../vf/ast/ApexClassPropertyTypesTest.java | 4 +- .../pmd/lang/vf/ast/ObjectFieldTypesTest.java | 8 +- .../pmd/lang/xml/XmlParserOptions.java | 4 +- .../pmd/lang/xml/XmlParserOptionsTest.java | 13 -- 25 files changed, 182 insertions(+), 459 deletions(-) delete mode 100644 pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexParserOptions.java delete mode 100644 pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java delete mode 100644 pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java delete mode 100644 pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java index fdee79b4a3..285d0e295c 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java @@ -32,11 +32,6 @@ public class ApexHandler extends AbstractPmdLanguageVersionHandler { return ApexRuleViolationFactory.INSTANCE; } - @Override - public ParserOptions getDefaultParserOptions() { - return new ApexParserOptions(); - } - @Override public Parser getParser(ParserOptions parserOptions) { return new ApexParser(); diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexParserOptions.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexParserOptions.java deleted file mode 100644 index 8d41b5d1ba..0000000000 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexParserOptions.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.apex; - -import net.sourceforge.pmd.lang.ParserOptions; - -/** - * @deprecated Not useful - */ -@Deprecated -public class ApexParserOptions extends ParserOptions { - - // empty class for now, since we don't have extra options for Apex - // Once you add something here, make sure to override hashCode and equals - -} diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java index 6c8ca348c6..0606311632 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/rule/AbstractApexRule.java @@ -6,9 +6,7 @@ package net.sourceforge.pmd.lang.apex.rule; import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.apex.ApexLanguageModule; -import net.sourceforge.pmd.lang.apex.ApexParserOptions; import net.sourceforge.pmd.lang.apex.ast.ApexParserVisitor; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; @@ -20,11 +18,6 @@ public abstract class AbstractApexRule extends AbstractRule super.setLanguage(LanguageRegistry.getLanguage(ApexLanguageModule.NAME)); } - @Override - public ParserOptions getParserOptions() { - return new ApexParserOptions(); - } - @Override public void apply(Node target, RuleContext ctx) { target.acceptVisitor(this, ctx); 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 5224cb7f38..e0bed7e008 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMD.java @@ -22,7 +22,6 @@ 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; @@ -36,9 +35,6 @@ import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageFilenameFilter; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.LanguageVersionDiscoverer; -import net.sourceforge.pmd.lang.LanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; -import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.processor.AbstractPMDProcessor; import net.sourceforge.pmd.processor.MonoThreadProcessor; import net.sourceforge.pmd.processor.MultiThreadProcessor; @@ -160,31 +156,6 @@ public class PMD { return dataSources; } - /** - * Helper method to get a configured parser for the requested language. The - * parser is configured based on the given {@link PMDConfiguration}. - * - * @param languageVersion - * the requested language - * @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. - LanguageVersionHandler languageVersionHandler = languageVersion.getLanguageVersionHandler(); - ParserOptions options = languageVersionHandler.getDefaultParserOptions(); - if (configuration != null) { - options.setSuppressMarker(configuration.getSuppressMarker()); - } - return languageVersionHandler.getParser(options); - } - /** * Get the runtime configuration. The configuration can be modified to * affect how PMD behaves. 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 bf6599f520..fec1de009d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java @@ -176,10 +176,11 @@ public class SourceCodeProcessor { languageVersion, filename, sourceCode, - SemanticErrorReporter.noop(), // TODO - configuration.getSuppressMarker() + SemanticErrorReporter.noop() // TODO ); + task.getProperties().setProperty(ParserTask.COMMENT_MARKER, configuration.getSuppressMarker()); + Parser parser = languageVersion.getLanguageVersionHandler().getParser(); RootNode rootNode = parse(parser, task); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java index 572492d864..0444155638 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java @@ -6,10 +6,13 @@ package net.sourceforge.pmd.lang; import java.util.Collections; import java.util.List; +import java.util.Locale; import org.apache.commons.lang3.EnumUtils; import net.sourceforge.pmd.lang.ast.AstProcessingStage; +import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertySource; /** @@ -53,4 +56,48 @@ public abstract class AbstractPmdLanguageVersionHandler extends AbstractLanguage } + + /** + * Returns the environment variable name that a user can set in order to override the default value. + */ + String getEnvironmentVariableName(String langTerseName, PropertyDescriptor propertyDescriptor) { + if (langTerseName == null) { + throw new IllegalStateException("Language is null"); + } + return "PMD_" + langTerseName.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. + */ + + String getEnvValue(String langTerseName, PropertyDescriptor propertyDescriptor) { + // note: since we use environent variables and not system properties, + // tests override this method. + return System.getenv(getEnvironmentVariableName(langTerseName, 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 + */ + @Deprecated + protected final void overridePropertiesFromEnv(String langTerseName, PropertySource source) { + for (PropertyDescriptor propertyDescriptor : source.getPropertyDescriptors()) { + String propertyValue = getEnvValue(langTerseName, propertyDescriptor); + + if (propertyValue != null) { + setPropertyCapture(source, propertyDescriptor, propertyValue); + } + } + } + + @Deprecated + private void setPropertyCapture(PropertySource source, PropertyDescriptor propertyDescriptor, String propertyValue) { + T value = propertyDescriptor.valueFrom(propertyValue); + source.setProperty(propertyDescriptor, value); + } + } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java index 834eaa36f4..d353230e77 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java @@ -15,6 +15,7 @@ import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory; import net.sourceforge.pmd.lang.rule.xpath.impl.XPathHandler; +import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.util.designerbindings.DesignerBindings; import net.sourceforge.pmd.util.designerbindings.DesignerBindings.DefaultDesignerBindings; @@ -56,6 +57,10 @@ public interface LanguageVersionHandler { return new ParserOptions(); } + default void declareParserTaskProperties(PropertySource source) { + // do nothing + } + /** * Get the Parser. 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 b92d9c7b67..5028589bc0 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,12 @@ package net.sourceforge.pmd.lang; -import java.util.Locale; import java.util.Objects; -import org.checkerframework.checker.nullness.qual.NonNull; - import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.properties.AbstractPropertySource; import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.lang.ast.Parser; /** * Represents a set of configuration options for a {@link Parser}. For each @@ -40,15 +37,6 @@ public class ParserOptions { this.parserOptionsProperties = new ParserOptionsProperties(); } - public final @NonNull String getSuppressMarker() { - return suppressMarker; - } - - public final void setSuppressMarker(@NonNull String suppressMarker) { - Objects.requireNonNull(suppressMarker); - this.suppressMarker = suppressMarker; - } - protected final void defineProperty(PropertyDescriptor propertyDescriptor) { parserOptionsProperties.definePropertyDescriptor(propertyDescriptor); } @@ -85,45 +73,6 @@ public class ParserOptions { 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 (languageId == null) { - throw new IllegalStateException("Language is null"); - } - return "PMD_" + languageId.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. - */ - String getEnvValue(PropertyDescriptor propertyDescriptor) { - // note: since we use environent variables and not system properties, - // tests override this method. - 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 final void overridePropertiesFromEnv() { - for (PropertyDescriptor propertyDescriptor : parserOptionsProperties.getPropertyDescriptors()) { - String propertyValue = getEnvValue(propertyDescriptor); - - if (propertyValue != null) { - 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 { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java index bd125169a1..9aa9fbdb77 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java @@ -10,6 +10,10 @@ import org.checkerframework.checker.nullness.qual.NonNull; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.properties.AbstractPropertySource; +import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertyFactory; +import net.sourceforge.pmd.properties.PropertySource; /** * Produces an AST from a source file. Instances of this interface must @@ -47,7 +51,7 @@ public interface Parser { private final String sourceText; private final SemanticErrorReporter reporter; - private final String commentMarker; + private final PropertySource propertySource; public ParserTask(LanguageVersion lv, String filepath, String sourceText, SemanticErrorReporter reporter) { @@ -59,7 +63,20 @@ public interface Parser { this.filepath = Objects.requireNonNull(filepath, "filepath was null"); this.sourceText = Objects.requireNonNull(sourceText, "sourceText was null"); this.reporter = Objects.requireNonNull(reporter, "reporter was null"); - this.commentMarker = Objects.requireNonNull(commentMarker, "commentMarker was null"); + + this.propertySource = new ParserTaskProperties(); + propertySource.definePropertyDescriptor(COMMENT_MARKER); + } + + public static final PropertyDescriptor COMMENT_MARKER = + PropertyFactory.stringProperty("suppressionCommentMarker") + .desc("deprecated! NOPMD") + .defaultValue(PMD.SUPPRESS_MARKER) + .build(); + + @Deprecated // transitional until language properties are implemented + public PropertySource getProperties() { + return propertySource; } @@ -93,7 +110,39 @@ public interface Parser { * The suppression marker for comments. */ public @NonNull String getCommentMarker() { - return commentMarker; + return getProperties().getProperty(COMMENT_MARKER); + } + + + private static final class ParserTaskProperties extends AbstractPropertySource { + + @Override + protected String getPropertySourceType() { + return "ParserOptions"; + } + + @Override + public String getName() { + return "n/a"; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ParserTaskProperties)) { + return false; + } + final ParserTaskProperties that = (ParserTaskProperties) obj; + return Objects.equals(getPropertiesByPropertyDescriptor(), + that.getPropertiesByPropertyDescriptor()); + } + + @Override + public int hashCode() { + return getPropertiesByPropertyDescriptor().hashCode(); + } } } 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 80424834d3..e04e9726fe 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java @@ -9,7 +9,7 @@ import static net.sourceforge.pmd.lang.ast.test.TestUtilsKt.assertSuppressed; import org.junit.Test; -import net.sourceforge.pmd.lang.ParserOptions; +import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest; @@ -38,9 +38,7 @@ public class ExcludeLinesTest extends BaseNonParserTest { @Test public void testAlternateMarker() { - ParserOptions options = new ParserOptions(); - options.setSuppressMarker("FOOBAR"); - Report rpt = java.withParserOptions(options).executeRule(getRule(), TEST3); + Report rpt = java.withParserConfig(p -> p.setProperty(ParserTask.COMMENT_MARKER, "FOOBAR")).executeRule(getRule(), TEST3); assertSize(rpt, 0); assertSuppressed(rpt, 1); } diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptHandler.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptHandler.java index 100e47054a..e9b6091c03 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptHandler.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptHandler.java @@ -19,7 +19,7 @@ class EcmascriptHandler extends AbstractPmdLanguageVersionHandler { @Override public Parser getParser(ParserOptions parserOptions) { - return new EcmascriptParser(rhinoVersion, parserOptions.getSuppressMarker()); + return new EcmascriptParser(rhinoVersion); } } diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParser.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParser.java index 7c0e911099..f1c86ada3d 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParser.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParser.java @@ -16,7 +16,6 @@ import org.mozilla.javascript.ast.Comment; import org.mozilla.javascript.ast.ErrorCollector; import org.mozilla.javascript.ast.ParseProblem; -import net.sourceforge.pmd.internal.util.AssertionUtil; import net.sourceforge.pmd.lang.ast.AstInfo; import net.sourceforge.pmd.lang.ast.FileAnalysisException; import net.sourceforge.pmd.lang.ast.ParseException; @@ -24,11 +23,9 @@ import net.sourceforge.pmd.lang.ast.RootNode; public final class EcmascriptParser implements net.sourceforge.pmd.lang.ast.Parser { private final int esVersion; - private final String suppressMarker; - public EcmascriptParser(int version, String suppressMarker) { + public EcmascriptParser(int version) { this.esVersion = version; - this.suppressMarker = AssertionUtil.requireParamNotNull("suppression marker", suppressMarker); } private AstRoot parseEcmascript(final String sourceCode, final List parseProblems) throws ParseException { @@ -61,6 +58,7 @@ public final class EcmascriptParser implements net.sourceforge.pmd.lang.ast.Pars final EcmascriptTreeBuilder treeBuilder = new EcmascriptTreeBuilder(sourceCode, parseProblems); final ASTAstRoot tree = (ASTAstRoot) treeBuilder.build(astRoot); + String suppressMarker = task.getCommentMarker(); Map suppressMap = new HashMap<>(); if (astRoot.getComments() != null) { for (Comment comment : astRoot.getComments()) { diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java index 91b849dc29..3e9331913f 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java @@ -16,8 +16,8 @@ import org.mozilla.javascript.ast.AstRoot; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.Report; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.ecmascript.rule.AbstractEcmascriptRule; public class EcmascriptParserTest extends EcmascriptParserTestBase { @@ -150,9 +150,8 @@ public class EcmascriptParserTest extends EcmascriptParserTestBase { assertEquals(" I know what I'm doing", root.getAstInfo().getSuppressionComments().get(2)); assertEquals(1, root.getAstInfo().getSuppressionComments().size()); - ParserOptions parserOptions = new ParserOptions(); - parserOptions.setSuppressMarker("FOOOO"); - root = js.withParserOptions(parserOptions).parse("function(x) {\n" + "y = y; //NOPMD xyz\n" + "x = x; //FOOOO I know what I'm doing\n" + "}\n"); + root = js.withParserConfig(p -> p.setProperty(ParserTask.COMMENT_MARKER, "FOOOO")) + .parse("function(x) {\n" + "y = y; //NOPMD xyz\n" + "x = x; //FOOOO I know what I'm doing\n" + "}\n"); assertEquals(" I know what I'm doing", root.getAstInfo().getSuppressionComments().get(3)); assertEquals(1, root.getAstInfo().getSuppressionComments().size()); } diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index 0fa6ec2b80..bae3c45066 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -6,12 +6,14 @@ package net.sourceforge.pmd.lang.ast.test import net.sourceforge.pmd.* import net.sourceforge.pmd.lang.* import net.sourceforge.pmd.lang.ast.* +import net.sourceforge.pmd.properties.PropertySource import net.sourceforge.pmd.util.datasource.DataSource import org.apache.commons.io.IOUtils import java.io.File import java.io.InputStream import java.io.StringReader import java.nio.charset.StandardCharsets +import java.util.function.Consumer /** * Language-independent base for a parser utils class. @@ -28,7 +30,8 @@ abstract class BaseParsingHelper, T : RootNode val defaultVerString: String?, val resourceLoader: Class<*>?, val resourcePrefix: String, - val parserOptions: ParserOptions? = null + val parserOptions: ParserOptions? = null, + val configureParser: (PropertySource) -> Unit = {}, ) { companion object { @@ -98,6 +101,10 @@ abstract class BaseParsingHelper, T : RootNode clone(params.copy(parserOptions = parserOptions)) + fun withParserConfig(configFun: Consumer): Self = + clone(params.copy(configureParser = { configFun.accept(it) })) + + fun getHandler(version: String): LanguageVersionHandler { return getVersion(version).languageVersionHandler } @@ -123,7 +130,11 @@ abstract class BaseParsingHelper, T : RootNode val parser = handler.getParser(options) val source = DataSource.forString(sourceCode, FileAnalysisException.NO_FILE_NAME) val toString = DataSource.readToString(source, StandardCharsets.UTF_8) - val task = Parser.ParserTask(lversion, FileAnalysisException.NO_FILE_NAME, toString, SemanticErrorReporter.noop(), options.suppressMarker) + val task = Parser.ParserTask(lversion, FileAnalysisException.NO_FILE_NAME, toString, SemanticErrorReporter.noop()) + task.properties.also { + handler.declareParserTaskProperties(it) + params.configureParser(it) + } val rootNode = rootClass.cast(parser.parse(task)) if (params.doProcess) { postProcessing(handler, lversion, rootNode) @@ -148,7 +159,7 @@ abstract class BaseParsingHelper, T : RootNode override fun getLanguageVersion(): LanguageVersion = lversion } - val stages = selectProcessingStages(handler).sortedWith(Comparator { o1, o2 -> o1.compare(o2) }) + val stages = selectProcessingStages(handler).sortedWith { o1, o2 -> o1.compare(o2) } stages.forEach { it.processAST(rootNode, astAnalysisContext) @@ -212,7 +223,7 @@ abstract class BaseParsingHelper, T : RootNode @JvmOverloads fun executeRule(rule: Rule, code: String, filename: String = "testfile.${language.extensions[0]}"): Report { val p = PMD() - p.configuration.suppressMarker = this.params.parserOptions?.suppressMarker ?: PMD.SUPPRESS_MARKER + // p.configuration.suppressMarker = this.params.parserOptions?.suppressMarker ?: PMD.SUPPRESS_MARKER val ctx = RuleContext() val report = Report() ctx.report = report 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 index 131c369b1e..dc85fc706e 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java @@ -4,9 +4,6 @@ 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}. @@ -14,22 +11,6 @@ import org.junit.Test; @Deprecated public class ParserOptionsTest { - /** - * 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. 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 deleted file mode 100644 index 8fc99fe8d5..0000000000 --- a/pmd-test/src/test/java/net/sourceforge/pmd/lang/ParserOptionsUnitTest.java +++ /dev/null @@ -1,206 +0,0 @@ -/** - * 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 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.PMD; -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} - * - * TODO: 7.0.0: Rename to ParserOptionsTest when {@link ParserOptionsTest} is removed. - */ -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"); - 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(DummyLanguageModule.TERSE_NAME); - defineProperty(LIST_DESCRIPTOR); - defineProperty(STRING_DESCRIPTOR); - overridePropertiesFromEnv(); - } - } - - /** - * SuppressMarker should be initially null and changeable. - */ - @Test - public void testSuppressMarker() { - ParserOptions parserOptions = new ParserOptions(); - Assert.assertEquals(PMD.SUPPRESS_MARKER, parserOptions.getSuppressMarker()); - parserOptions.setSuppressMarker("foo"); - Assert.assertEquals("foo", parserOptions.getSuppressMarker()); - } - - @Test - public void testDefaultPropertyDescriptors() { - TestParserOptions parserOptions = new TestParserOptions(); - 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.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)); - } - - /** - * Verify that the equals and hashCode methods work as expected. - * TODO: Consider using Guava's EqualsTester - */ - @Test - public void testSuppressMarkerEqualsHashCode() { - ParserOptions options1; - ParserOptions options2; - ParserOptions options3; - ParserOptions options4; - - // 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); - - // PropertyDescriptor - options1 = new ParserOptions(); - options2 = new ParserOptions(); - options3 = new ParserOptions(); - options4 = new ParserOptions(); - 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 - options1 = new ParserOptions(); - options2 = new ParserOptions(); - options3 = new ParserOptions(); - options4 = new ParserOptions(); - 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(DummyLanguageModule.TERSE_NAME); - options2 = new ParserOptions(); - options3 = new ParserOptions(DummyLanguageModule.TERSE_NAME); - options4 = new ParserOptions(); - ParserOptionsTestUtils.verifyOptionsEqualsHashcode(options1, options2, options3, options4); - - // SuppressMarker, PropertyDescriptor, PropertyValue, Language - options1 = new ParserOptions(DummyLanguageModule.TERSE_NAME); - options2 = new ParserOptions(); - options3 = new ParserOptions(DummyLanguageModule.TERSE_NAME); - options4 = new ParserOptions(); - options1.setSuppressMarker("foo"); - options2.setSuppressMarker("bar"); - options3.setSuppressMarker("foo"); - options4.setSuppressMarker("bar"); - 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)); - } - - @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/VfHandler.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfHandler.java index 34b9f1ab2c..97248ffeaa 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 @@ -4,20 +4,56 @@ package net.sourceforge.pmd.lang.vf; +import java.io.File; +import java.util.Collections; +import java.util.List; + import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.vf.ast.VfParser; +import net.sourceforge.pmd.properties.PropertyDescriptor; +import net.sourceforge.pmd.properties.PropertyFactory; +import net.sourceforge.pmd.properties.PropertySource; public class VfHandler extends AbstractPmdLanguageVersionHandler { + 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. + * + *

      Env variable is {@code PMD_VF_APEXDIRECTORIES}. + */ + 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. + * + *

      Env variable is {@code PMD_VF_OBJECTSDIRECTORIES}. + */ + public static final PropertyDescriptor> OBJECTS_DIRECTORIES_DESCRIPTOR = + PropertyFactory.stringListProperty("objectsDirectories") + .desc("Location of Custom Object directories. Absolute or relative to the Visualforce directory.") + .defaultValue(DEFAULT_OBJECT_DIRECTORIES) + .delim(',') + .build(); + @Override public Parser getParser(ParserOptions parserOptions) { return new VfParser(); } @Override - public ParserOptions getDefaultParserOptions() { - return new VfParserOptions(); + public void declareParserTaskProperties(PropertySource source) { + source.definePropertyDescriptor(APEX_DIRECTORIES_DESCRIPTOR); + source.definePropertyDescriptor(OBJECTS_DIRECTORIES_DESCRIPTOR); + overridePropertiesFromEnv(VfLanguageModule.TERSE_NAME, source); } } 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 deleted file mode 100644 index 16ba794e88..0000000000 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/VfParserOptions.java +++ /dev/null @@ -1,49 +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.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. - * - *

      Env variable is {@code PMD_VF_APEXDIRECTORIES}. - */ - 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. - * - *

      Env variable is {@code PMD_VF_OBJECTSDIRECTORIES}. - */ - public static final PropertyDescriptor> OBJECTS_DIRECTORIES_DESCRIPTOR = - PropertyFactory.stringListProperty("objectsDirectories") - .desc("Location of Custom Object directories. Absolute or relative to the Visualforce directory.") - .defaultValue(DEFAULT_OBJECT_DIRECTORIES) - .delim(',') - .build(); - - public VfParserOptions() { - super(VfLanguageModule.TERSE_NAME); - defineProperty(APEX_DIRECTORIES_DESCRIPTOR); - defineProperty(OBJECTS_DIRECTORIES_DESCRIPTOR); - overridePropertiesFromEnv(); - } -} diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java index 532c0f0eb4..b7a097c305 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java @@ -13,8 +13,9 @@ import java.util.logging.Logger; import org.apache.commons.lang3.StringUtils; +import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.vf.DataType; -import net.sourceforge.pmd.lang.vf.VfParserOptions; +import net.sourceforge.pmd.lang.vf.VfHandler; /** * Visits {@link ASTExpression} nodes and stores type information for @@ -43,10 +44,10 @@ class VfExpressionTypeVisitor extends VfParserVisitorAdapter { private final List apexDirectories; private final List objectsDirectories; - VfExpressionTypeVisitor(String fileName, VfParserOptions propertySource) { - this.fileName = fileName; - this.apexDirectories = propertySource.getProperty(VfParserOptions.APEX_DIRECTORIES_DESCRIPTOR); - this.objectsDirectories = propertySource.getProperty(VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR); + VfExpressionTypeVisitor(ParserTask task) { + this.fileName = task.getFileDisplayName(); + this.apexDirectories = task.getProperties().getProperty(VfHandler.APEX_DIRECTORIES_DESCRIPTOR); + this.objectsDirectories = task.getProperties().getProperty(VfHandler.OBJECTS_DIRECTORIES_DESCRIPTOR); this.apexClassNames = new ArrayList<>(); this.apexClassPropertyTypes = new ApexClassPropertyTypes(); this.objectFieldTypes = new ObjectFieldTypes(); diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfParser.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfParser.java index 571b7bf37c..f50d4bbeec 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfParser.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfParser.java @@ -10,7 +10,6 @@ import net.sourceforge.pmd.lang.ast.CharStream; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument; import net.sourceforge.pmd.lang.ast.impl.javacc.JjtreeParserAdapter; -import net.sourceforge.pmd.lang.vf.VfParserOptions; /** * Parser for the VisualForce language. 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 deleted file mode 100644 index 5c238b1157..0000000000 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/VfParserOptionsTest.java +++ /dev/null @@ -1,22 +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 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)); - assertEquals(VfParserOptions.DEFAULT_OBJECT_DIRECTORIES, - vfParserOptions.getProperty(VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR)); - } - -} diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesTest.java index d8e14a70b8..ca0204d928 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesTest.java @@ -17,7 +17,7 @@ import org.junit.Test; import net.sourceforge.pmd.lang.vf.DataType; import net.sourceforge.pmd.lang.vf.VFTestUtils; -import net.sourceforge.pmd.lang.vf.VfParserOptions; +import net.sourceforge.pmd.lang.vf.VfHandler; public class ApexClassPropertyTypesTest { private static final Map EXPECTED_DATA_TYPES; @@ -58,7 +58,7 @@ public class ApexClassPropertyTypesTest { ApexClassPropertyTypes apexClassPropertyTypes = new ApexClassPropertyTypes(); ObjectFieldTypesTest.validateDataTypes(EXPECTED_DATA_TYPES, apexClassPropertyTypes, vfPagePath, - VfParserOptions.APEX_DIRECTORIES_DESCRIPTOR.defaultValue()); + VfHandler.APEX_DIRECTORIES_DESCRIPTOR.defaultValue()); } @Test diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypesTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypesTest.java index 899b2f7012..26de0aac96 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypesTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ObjectFieldTypesTest.java @@ -19,7 +19,7 @@ import org.junit.Test; import net.sourceforge.pmd.lang.vf.DataType; import net.sourceforge.pmd.lang.vf.VFTestUtils; -import net.sourceforge.pmd.lang.vf.VfParserOptions; +import net.sourceforge.pmd.lang.vf.VfHandler; public class ObjectFieldTypesTest { private static final Map EXPECTED_SFDX_DATA_TYPES; @@ -57,7 +57,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, VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue()); + validateSfdxAccount(objectFieldTypes, vfPagePath, VfHandler.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue()); } /** @@ -68,7 +68,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, VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue()); + validateMDAPIAccount(objectFieldTypes, vfPagePath, VfHandler.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue()); } /** @@ -80,7 +80,7 @@ public class ObjectFieldTypesTest { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) .resolve("SomePage.page"); - List paths = Arrays.asList(VfParserOptions.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue().get(0), + List paths = Arrays.asList(VfHandler.OBJECTS_DIRECTORIES_DESCRIPTOR.defaultValue().get(0), VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.MDAPI, VFTestUtils.MetadataType.Objects).toString()); objectFieldTypes = new ObjectFieldTypes(); validateSfdxAccount(objectFieldTypes, vfPagePath, paths); diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java index b752d32b90..61c11c6692 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.xml; import java.io.ByteArrayInputStream; -import java.util.Objects; import org.xml.sax.EntityResolver; import org.xml.sax.InputSource; @@ -199,8 +198,7 @@ public class XmlParserOptions extends ParserOptions { return false; } final XmlParserOptions that = (XmlParserOptions) obj; - return Objects.equals(this.getSuppressMarker(), that.getSuppressMarker()) - && this.coalescing == that.coalescing && this.expandEntityReferences == that.expandEntityReferences + return this.coalescing == that.coalescing && this.expandEntityReferences == that.expandEntityReferences && this.ignoringComments == that.ignoringComments && this.ignoringElementContentWhitespace == that.ignoringElementContentWhitespace && this.namespaceAware == that.namespaceAware && this.validating == that.validating 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 547ea01bbe..6c571aec7c 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 @@ -6,7 +6,6 @@ package net.sourceforge.pmd.lang.xml; import static net.sourceforge.pmd.lang.ParserOptionsTestUtils.verifyOptionsEqualsHashcode; import static net.sourceforge.pmd.util.CollectionUtil.listOf; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -86,9 +85,6 @@ public class XmlParserOptionsTest { public void testSetters() { XmlParserOptions options = new XmlParserOptions(); - options.setSuppressMarker("foo"); - assertEquals("foo", options.getSuppressMarker()); - options.setCoalescing(true); assertTrue(options.isCoalescing()); options.setCoalescing(false); @@ -148,15 +144,6 @@ public class XmlParserOptionsTest { verifyOptionsEqualsHashcode(options1, options2, options3, options4); } - XmlParserOptions options1 = new XmlParserOptions(); - options1.setSuppressMarker("foo"); - XmlParserOptions options2 = new XmlParserOptions(); - options2.setSuppressMarker("bar"); - XmlParserOptions options3 = new XmlParserOptions(); - options3.setSuppressMarker("foo"); - XmlParserOptions options4 = new XmlParserOptions(); - options4.setSuppressMarker("bar"); - verifyOptionsEqualsHashcode(options1, options2, options3, options4); } private static final class MyRule extends AbstractXmlRule { From 70079f0842ed16d92ccaf6234104da5c6c791590 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 23:01:06 +0100 Subject: [PATCH 292/299] Remove XML dom rule --- .../pmd/lang/xml/rule/AbstractDomXmlRule.java | 131 ----------- .../lang/xml/rule/AbstractDomXmlRuleTest.java | 213 ------------------ 2 files changed, 344 deletions(-) delete mode 100644 pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRule.java delete mode 100644 pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRuleTest.java diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRule.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRule.java deleted file mode 100644 index 13f3ebb65a..0000000000 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRule.java +++ /dev/null @@ -1,131 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xml.rule; - -import org.w3c.dom.Attr; -import org.w3c.dom.CharacterData; -import org.w3c.dom.Comment; -import org.w3c.dom.Document; -import org.w3c.dom.DocumentType; -import org.w3c.dom.Element; -import org.w3c.dom.Entity; -import org.w3c.dom.EntityReference; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.Notation; -import org.w3c.dom.ProcessingInstruction; -import org.w3c.dom.Text; - -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.xml.ast.XmlNode; - -/** - * This is a base class for XML Java bases rules that which to visit the nodes - * using the DOM. Subclasses should override the DOM appropriate method and can - * call super to visit children. - */ -public class AbstractDomXmlRule extends AbstractXmlRule { - - @Override - protected void visit(XmlNode node, RuleContext ctx) { - final Node domNode = node.getNode(); - - // Visit the node - visitDomNode(node, domNode, ctx); - - // Visit attributes - visitAttributeNodes(node, domNode, ctx); - } - - protected void visitDomNode(XmlNode node, Node domNode, RuleContext ctx) { - switch (domNode.getNodeType()) { - case Node.CDATA_SECTION_NODE: - visit(node, (CharacterData) domNode, ctx); - break; - case Node.COMMENT_NODE: - visit(node, (Comment) domNode, ctx); - break; - case Node.DOCUMENT_NODE: - visit(node, (Document) domNode, ctx); - break; - case Node.DOCUMENT_TYPE_NODE: - visit(node, (DocumentType) domNode, ctx); - break; - case Node.ELEMENT_NODE: - visit(node, (Element) domNode, ctx); - break; - case Node.ENTITY_NODE: - visit(node, (Entity) domNode, ctx); - break; - case Node.ENTITY_REFERENCE_NODE: - visit(node, (EntityReference) domNode, ctx); - break; - case Node.NOTATION_NODE: - visit(node, (Notation) domNode, ctx); - break; - case Node.PROCESSING_INSTRUCTION_NODE: - visit(node, (ProcessingInstruction) domNode, ctx); - break; - case Node.TEXT_NODE: - visit(node, (Text) domNode, ctx); - break; - default: - throw new RuntimeException("Unexpected node type: " + domNode.getNodeType() + " on node: " + domNode); - } - } - - protected void visitAttributeNodes(XmlNode node, Node domNode, RuleContext ctx) { - NamedNodeMap attributes = domNode.getAttributes(); - if (attributes != null) { - for (int i = 0; i < attributes.getLength(); i++) { - visit(node, (Attr) attributes.item(i), ctx); - } - } - } - - protected void visit(XmlNode node, Attr attr, RuleContext ctx) { - // does nothing by default since attributes are leaf nodes - } - - protected void visit(XmlNode node, CharacterData characterData, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, Comment comment, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, Document document, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, DocumentType documentType, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, Element element, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, Entity entity, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, EntityReference entityReference, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, Notation notation, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, ProcessingInstruction processingInstruction, RuleContext ctx) { - super.visit(node, ctx); - } - - protected void visit(XmlNode node, Text text, RuleContext ctx) { - super.visit(node, ctx); - } -} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRuleTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRuleTest.java deleted file mode 100644 index 19d8ea3c70..0000000000 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractDomXmlRuleTest.java +++ /dev/null @@ -1,213 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xml.rule; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.junit.Test; -import org.w3c.dom.Attr; -import org.w3c.dom.CharacterData; -import org.w3c.dom.Comment; -import org.w3c.dom.Document; -import org.w3c.dom.DocumentType; -import org.w3c.dom.Element; -import org.w3c.dom.Entity; -import org.w3c.dom.EntityReference; -import org.w3c.dom.Notation; -import org.w3c.dom.ProcessingInstruction; -import org.w3c.dom.Text; - -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.xml.XmlParserOptions; -import net.sourceforge.pmd.lang.xml.XmlParsingHelper; -import net.sourceforge.pmd.lang.xml.ast.XmlNode; - -public class AbstractDomXmlRuleTest { - - @Test - public void testVisit() throws Exception { - String source = "]>TEXT>&entity;<"; - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setExpandEntityReferences(false); - - XmlNode xmlNode = XmlParsingHelper.XML.withParserOptions(parserOptions).parse(source); - MyRule rule = new MyRule(); - rule.apply(xmlNode, null); - - List visited = rule.visitedNodes.get("Attr"); - assertEquals(1, visited.size()); - assertEquals("abc", visited.get(0).getLocalName()); - - visited = rule.visitedNodes.get("CharacterData"); - assertEquals(1, visited.size()); - assertEquals("cdata!", ((CharacterData) visited.get(0)).getData()); - - visited = rule.visitedNodes.get("Comment"); - assertEquals("Comment", ((Comment) visited.get(0)).getData()); - - visited = rule.visitedNodes.get("Document"); - assertEquals(1, visited.size()); - - visited = rule.visitedNodes.get("DocumentType"); - assertEquals("testDoc", ((DocumentType) visited.get(0)).getName()); - - visited = rule.visitedNodes.get("Element"); - assertEquals(2, visited.size()); - assertEquals("foo", visited.get(0).getLocalName()); - assertEquals("bar", visited.get(1).getLocalName()); - - // TODO Figure out how to trigger this. - // visited = rule.visitedNodes.get("Entity"); - // assertEquals(0, visited.size()); - - visited = rule.visitedNodes.get("EntityReference"); - assertEquals(1, visited.size()); - assertEquals("entity", ((EntityReference) visited.get(0)).getNodeName()); - - // TODO Figure out how to trigger this. - // visited = rule.visitedNodes.get("Notation"); - // assertEquals(0, visited.size()); - - visited = rule.visitedNodes.get("ProcessingInstruction"); - assertEquals(1, visited.size()); - assertEquals("mypi", ((ProcessingInstruction) visited.get(0)).getTarget()); - - visited = rule.visitedNodes.get("Text"); - assertEquals(3, visited.size()); - assertEquals("TEXT", ((Text) visited.get(0)).getData()); - assertEquals(">", ((Text) visited.get(1)).getData()); - String text = ((Text) visited.get(2)).getData(); - if ("<".equals(text)) { - // java13 and later don't expand entities if setExpandEntityReferences==false - assertEquals("<", ((Text) visited.get(2)).getData()); - } else { - assertEquals("e<", ((Text) visited.get(2)).getData()); - } - } - - @Test - public void dtdIsNotLookedUp() { - String source = "" + ""; - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setLookupDescriptorDoc(false); - XmlNode xmlNode = XmlParsingHelper.XML.withParserOptions(parserOptions).parse(source); - // no exception should be thrown - - MyRule rule = new MyRule(); - rule.apply(xmlNode, null); - - // first element is still parsed - assertNotNull(rule.visitedNodes.get("Element")); - - } - - @Test - public void xsdIsNotLookedUp() { - String source = " " - + "" + ""; - XmlNode xmlNode = XmlParsingHelper.XML.parse(source); - // no exception should be thrown - // first element is still parsed - MyRule rule = new MyRule(); - rule.apply(xmlNode, null); - - assertNotNull(rule.visitedNodes.get("Element")); - - } - - private static class MyRule extends AbstractDomXmlRule { - final Map> visitedNodes = new HashMap<>(); - - MyRule() { - - } - - private void visit(String key, org.w3c.dom.Node node) { - List nodes = visitedNodes.get(key); - if (nodes == null) { - nodes = new ArrayList<>(); - visitedNodes.put(key, nodes); - } - nodes.add(node); - } - - @Override - protected void visit(XmlNode node, Attr attr, RuleContext ctx) { - visit("Attr", attr); - super.visit(node, attr, ctx); - } - - @Override - protected void visit(XmlNode node, CharacterData characterData, RuleContext ctx) { - visit("CharacterData", characterData); - super.visit(node, characterData, ctx); - } - - @Override - protected void visit(XmlNode node, Comment comment, RuleContext ctx) { - visit("Comment", comment); - super.visit(node, comment, ctx); - } - - @Override - protected void visit(XmlNode node, Document document, RuleContext ctx) { - visit("Document", document); - super.visit(node, document, ctx); - } - - @Override - protected void visit(XmlNode node, DocumentType documentType, RuleContext ctx) { - visit("DocumentType", documentType); - super.visit(node, documentType, ctx); - } - - @Override - protected void visit(XmlNode node, Element element, RuleContext ctx) { - visit("Element", element); - super.visit(node, element, ctx); - } - - @Override - protected void visit(XmlNode node, Entity entity, RuleContext ctx) { - visit("Entity", entity); - super.visit(node, entity, ctx); - } - - @Override - protected void visit(XmlNode node, EntityReference entityReference, RuleContext ctx) { - visit("EntityReference", entityReference); - super.visit(node, entityReference, ctx); - } - - @Override - protected void visit(XmlNode node, Notation notation, RuleContext ctx) { - visit("Notation", notation); - super.visit(node, notation, ctx); - } - - @Override - protected void visit(XmlNode node, ProcessingInstruction processingInstruction, RuleContext ctx) { - visit("ProcessingInstruction", processingInstruction); - super.visit(node, processingInstruction, ctx); - } - - @Override - protected void visit(XmlNode node, Text text, RuleContext ctx) { - visit("Text", text); - super.visit(node, text, ctx); - } - } -} From 163d7af6c28491b4e569c91661d165d8db57ebcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 23:18:00 +0100 Subject: [PATCH 293/299] Remove xml parser options --- .../pmd/lang/ast/test/NodePrinters.kt | 19 + .../pmd/lang/wsdl/rule/AbstractWsdlRule.java | 20 - .../sourceforge/pmd/lang/xml/XmlHandler.java | 5 - .../sourceforge/pmd/lang/xml/XmlParser.java | 2 +- .../pmd/lang/xml/XmlParserOptions.java | 209 --------- .../lang/xml/ast/internal/XmlParserImpl.java | 26 +- .../pmd/lang/xml/rule/AbstractXmlRule.java | 66 +-- .../lang/wsdl/rule/AbstractWsdlRuleTest.java | 51 --- .../pmd/lang/xml/XmlParserOptionsTest.java | 168 ------- .../pmd/lang/xml/XmlParserTest.java | 431 ------------------ .../pmd/lang/xml/ast/XmlCoordinatesTest.java | 58 +++ .../pmd/lang/xml/ast/XmlParserTest.java | 43 ++ .../lang/xml/rule/AbstractXmlRuleTest.java | 50 -- .../pmd/lang/xml/ast/testdata/bug1518.txt | 69 +++ .../{parsertests => ast/testdata}/bug1518.xml | 0 .../pmd/lang/xml/ast/testdata/sampleNs.txt | 22 + .../pmd/lang/xml/ast/testdata/sampleNs.xml | 21 + .../pmd/lang/xml/ast/testdata/sampleXml.txt | 20 + .../pmd/lang/xml/ast/testdata/sampleXml.xml | 9 + .../pmd/lang/xml/ast/testdata/xmlCoords.txt | 17 + .../pmd/lang/xml/ast/testdata/xmlCoords.xml | 22 + 21 files changed, 312 insertions(+), 1016 deletions(-) delete mode 100644 pmd-xml/src/main/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRule.java delete mode 100644 pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java delete mode 100644 pmd-xml/src/test/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRuleTest.java delete mode 100644 pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserOptionsTest.java delete mode 100644 pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserTest.java create mode 100644 pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlCoordinatesTest.java create mode 100644 pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlParserTest.java delete mode 100644 pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRuleTest.java create mode 100644 pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.txt rename pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/{parsertests => ast/testdata}/bug1518.xml (100%) create mode 100644 pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.txt create mode 100644 pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.xml create mode 100644 pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.txt create mode 100644 pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.xml create mode 100644 pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.txt create mode 100644 pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.xml diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodePrinters.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodePrinters.kt index 25ebe215a3..44ce65c6c2 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodePrinters.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/NodePrinters.kt @@ -33,6 +33,25 @@ open class RelevantAttributePrinter : BaseNodeAttributePrinter() { } +/** + * Only prints the begin/end coordinates. + */ +object CoordinatesPrinter : BaseNodeAttributePrinter() { + + private val Considered = setOf("BeginLine", "EndLine", "BeginColumn", "EndColumn") + + override fun fillAttributes(node: Node, result: MutableList) { + result += AttributeInfo("BeginLine", node.beginLine) + result += AttributeInfo("EndLine", node.endLine) + result += AttributeInfo("EndColumn", node.endColumn) + result += AttributeInfo("BeginColumn", node.beginColumn) + } + + override fun ignoreAttribute(node: Node, attribute: Attribute): Boolean = + attribute.name !in Considered + +} + /** * Base attribute printer, subclass to filter attributes. */ diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRule.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRule.java deleted file mode 100644 index e348d84583..0000000000 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRule.java +++ /dev/null @@ -1,20 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.wsdl.rule; - -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.wsdl.WsdlLanguageModule; -import net.sourceforge.pmd.lang.xml.rule.AbstractXmlRule; - -/** - * Created by bernardo-macedo on 24.06.15. - */ -public class AbstractWsdlRule extends AbstractXmlRule { - - public AbstractWsdlRule() { - super(LanguageRegistry.getLanguage(WsdlLanguageModule.NAME)); - } - -} diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlHandler.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlHandler.java index 97d79e8b5d..16280f3b27 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlHandler.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlHandler.java @@ -13,11 +13,6 @@ import net.sourceforge.pmd.lang.ParserOptions; */ public class XmlHandler extends AbstractPmdLanguageVersionHandler { - @Override - public ParserOptions getDefaultParserOptions() { - return new XmlParserOptions(); - } - @Override public Parser getParser(ParserOptions parserOptions) { return new XmlParser(parserOptions); diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParser.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParser.java index 7b3a9a2935..6fec27a40f 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParser.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParser.java @@ -23,7 +23,7 @@ public class XmlParser extends AbstractParser { @Override public RootXmlNode parse(String fileName, Reader source) throws ParseException { - return new XmlParserImpl((XmlParserOptions) parserOptions).parse(source); + return new XmlParserImpl().parse(source); } } diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java deleted file mode 100644 index b752d32b90..0000000000 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlParserOptions.java +++ /dev/null @@ -1,209 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xml; - -import java.io.ByteArrayInputStream; -import java.util.Objects; - -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; - -import net.sourceforge.pmd.Rule; -import net.sourceforge.pmd.lang.ParserOptions; -import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertyFactory; - -/** - * @deprecated Parser options will be removed with 7.0, these options - * will assume their default values then. - */ -@Deprecated -public class XmlParserOptions extends ParserOptions { - - // Note: The UI order values are chosen to be larger than those built into - // XPathRule. - public static final PropertyDescriptor COALESCING_DESCRIPTOR = - PropertyFactory.booleanProperty("coalescing") - .desc("deprecated!Specifies that the XML parser convert CDATA nodes to Text nodes and append it to the adjacent (if any) text node.") - .defaultValue(false) - .build(); - public static final PropertyDescriptor EXPAND_ENTITY_REFERENCES_DESCRIPTOR = - PropertyFactory.booleanProperty("expandEntityReferences") - .desc("deprecated!Specifies that the XML parser expand entity reference nodes.") - .defaultValue(true) - .build(); - public static final PropertyDescriptor IGNORING_COMMENTS_DESCRIPTOR = - PropertyFactory.booleanProperty("ignoringComments") - .desc("deprecated!Specifies that the XML parser ignore comments.") - .defaultValue(false) - .build(); - public static final PropertyDescriptor IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR = - PropertyFactory.booleanProperty("ignoringElementContentWhitespace") - .desc("deprecated!Specifies that the XML parser eliminate whitespace in element content. Setting this to 'true' will force validating.") - .defaultValue(false) - .build(); - public static final PropertyDescriptor NAMESPACE_AWARE_DESCRIPTOR = - PropertyFactory.booleanProperty("namespaceAware") - .desc("deprecated!Specifies that the XML parser will provide support for XML namespaces.") - .defaultValue(true) - .build(); - public static final PropertyDescriptor VALIDATING_DESCRIPTOR = - PropertyFactory.booleanProperty("validating") - .desc("deprecated!Specifies that the XML parser will validate documents as they are parsed. This only works for DTDs.") - .defaultValue(false) - .build(); - public static final PropertyDescriptor XINCLUDE_AWARE_DESCRIPTOR = - PropertyFactory.booleanProperty("xincludeAware") - .desc("deprecated!Specifies that the XML parser will process XInclude markup.") - .defaultValue(false) - .build(); - public static final PropertyDescriptor LOOKUP_DESCRIPTOR_DTD = - PropertyFactory.booleanProperty("xincludeAware") - .desc("deprecated!Specifies whether XML parser will attempt to lookup the DTD.") - .defaultValue(false) - .build(); - - public static final EntityResolver SILENT_ENTITY_RESOLVER = (publicId, systemId) -> new InputSource(new ByteArrayInputStream("".getBytes())); - - private boolean coalescing; - private boolean expandEntityReferences; - private boolean ignoringComments; - private boolean ignoringElementContentWhitespace; - private boolean namespaceAware; - private boolean validating; - private boolean xincludeAware; - private boolean lookupDescriptorDoc; - - public XmlParserOptions() { - this.coalescing = COALESCING_DESCRIPTOR.defaultValue(); - this.expandEntityReferences = EXPAND_ENTITY_REFERENCES_DESCRIPTOR.defaultValue(); - this.ignoringComments = IGNORING_COMMENTS_DESCRIPTOR.defaultValue(); - this.ignoringElementContentWhitespace = IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR.defaultValue(); - this.namespaceAware = NAMESPACE_AWARE_DESCRIPTOR.defaultValue(); - this.validating = VALIDATING_DESCRIPTOR.defaultValue(); - this.xincludeAware = XINCLUDE_AWARE_DESCRIPTOR.defaultValue(); - this.lookupDescriptorDoc = LOOKUP_DESCRIPTOR_DTD.defaultValue(); - } - - public XmlParserOptions(Rule rule) { - this.coalescing = rule.getProperty(COALESCING_DESCRIPTOR); - this.expandEntityReferences = rule.getProperty(EXPAND_ENTITY_REFERENCES_DESCRIPTOR); - this.ignoringComments = rule.getProperty(IGNORING_COMMENTS_DESCRIPTOR); - this.ignoringElementContentWhitespace = rule.getProperty(IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR); - this.namespaceAware = rule.getProperty(NAMESPACE_AWARE_DESCRIPTOR); - this.validating = rule.getProperty(VALIDATING_DESCRIPTOR); - this.xincludeAware = rule.getProperty(XINCLUDE_AWARE_DESCRIPTOR); - this.lookupDescriptorDoc = rule.getProperty(LOOKUP_DESCRIPTOR_DTD); - } - - /** - * - * @return the configured entity resolver. If {@link #lookupDescriptorDoc} - * is false it would normally force the XML parser to use its own - * resolver - */ - public EntityResolver getEntityResolver() { - if (!lookupDescriptorDoc) { - return SILENT_ENTITY_RESOLVER; - } else { - return null; - } - } - - public boolean isLookupDescriptorDoc() { - return lookupDescriptorDoc; - } - - public void setLookupDescriptorDoc(boolean lookupDescriptorDoc) { - this.lookupDescriptorDoc = lookupDescriptorDoc; - } - - public boolean isCoalescing() { - return this.coalescing; - } - - public void setCoalescing(boolean coalescing) { - this.coalescing = coalescing; - } - - public boolean isExpandEntityReferences() { - return this.expandEntityReferences; - } - - public void setExpandEntityReferences(boolean expandEntityReferences) { - this.expandEntityReferences = expandEntityReferences; - } - - public boolean isIgnoringComments() { - return this.ignoringComments; - } - - public void setIgnoringComments(boolean ignoringComments) { - this.ignoringComments = ignoringComments; - } - - public boolean isIgnoringElementContentWhitespace() { - return this.ignoringElementContentWhitespace; - } - - public void setIgnoringElementContentWhitespace(boolean ignoringElementContentWhitespace) { - this.ignoringElementContentWhitespace = ignoringElementContentWhitespace; - } - - public boolean isNamespaceAware() { - return this.namespaceAware; - } - - public void setNamespaceAware(boolean namespaceAware) { - this.namespaceAware = namespaceAware; - } - - public boolean isValidating() { - return this.validating; - } - - public void setValidating(boolean validating) { - this.validating = validating; - } - - public boolean isXincludeAware() { - return this.xincludeAware; - } - - public void setXincludeAware(boolean xincludeAware) { - this.xincludeAware = xincludeAware; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = super.hashCode(); - result = prime * result + (coalescing ? 1231 : 1237); - result = prime * result + (expandEntityReferences ? 1231 : 1237); - result = prime * result + (ignoringComments ? 1231 : 1237); - result = prime * result + (ignoringElementContentWhitespace ? 1231 : 1237); - result = prime * result + (namespaceAware ? 1231 : 1237); - result = prime * result + (validating ? 1231 : 1237); - result = prime * result + (xincludeAware ? 1231 : 1237); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final XmlParserOptions that = (XmlParserOptions) obj; - return Objects.equals(this.getSuppressMarker(), that.getSuppressMarker()) - && this.coalescing == that.coalescing && this.expandEntityReferences == that.expandEntityReferences - && this.ignoringComments == that.ignoringComments - && this.ignoringElementContentWhitespace == that.ignoringElementContentWhitespace - && this.namespaceAware == that.namespaceAware && this.validating == that.validating - && this.xincludeAware == that.xincludeAware; - } -} diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java index ddc433af6f..dd94aaa7ba 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/ast/internal/XmlParserImpl.java @@ -21,19 +21,12 @@ import org.xml.sax.SAXException; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.RootNode; -import net.sourceforge.pmd.lang.xml.XmlParserOptions; import net.sourceforge.pmd.lang.xml.ast.XmlNode; -public class XmlParserImpl { +public final class XmlParserImpl { - private final XmlParserOptions parserOptions; - private Map nodeCache = new HashMap<>(); - - - public XmlParserImpl(XmlParserOptions parserOptions) { - this.parserOptions = parserOptions; - } + private final Map nodeCache = new HashMap<>(); private Document parseDocument(String xmlData) throws ParseException { @@ -41,17 +34,16 @@ public class XmlParserImpl { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(parserOptions.isNamespaceAware()); - dbf.setValidating(parserOptions.isValidating()); - dbf.setIgnoringComments(parserOptions.isIgnoringComments()); - dbf.setIgnoringElementContentWhitespace(parserOptions.isIgnoringElementContentWhitespace()); - dbf.setExpandEntityReferences(parserOptions.isExpandEntityReferences()); - dbf.setCoalescing(parserOptions.isCoalescing()); - dbf.setXIncludeAware(parserOptions.isXincludeAware()); + dbf.setNamespaceAware(true); + dbf.setValidating(false); + dbf.setIgnoringComments(false); + dbf.setIgnoringElementContentWhitespace(false); + dbf.setExpandEntityReferences(true); + dbf.setCoalescing(false); + dbf.setXIncludeAware(false); dbf.setFeature("http://xml.org/sax/features/external-general-entities", false); dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); DocumentBuilder documentBuilder = dbf.newDocumentBuilder(); - documentBuilder.setEntityResolver(parserOptions.getEntityResolver()); return documentBuilder.parse(new InputSource(new StringReader(xmlData))); } catch (ParserConfigurationException | SAXException | IOException e) { throw new ParseException(e); diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java index 136ba4634a..6b2d9be243 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRule.java @@ -4,74 +4,12 @@ package net.sourceforge.pmd.lang.xml.rule; -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.ParserOptions; -import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; -import net.sourceforge.pmd.lang.xml.XmlLanguageModule; -import net.sourceforge.pmd.lang.xml.XmlParserOptions; -import net.sourceforge.pmd.lang.xml.ast.XmlNode; -import net.sourceforge.pmd.properties.PropertyDescriptor; /** - * This is a base class for XML Java bases rules. Subclasses should override - * {@link #visit(XmlNode, RuleContext)} and can call super to visit - * children. + * This is a base class for XML Java bases rules. */ -public class AbstractXmlRule extends AbstractRule { +public abstract class AbstractXmlRule extends AbstractRule { - @Deprecated - public static final PropertyDescriptor COALESCING_DESCRIPTOR = XmlParserOptions.COALESCING_DESCRIPTOR; - @Deprecated - public static final PropertyDescriptor EXPAND_ENTITY_REFERENCES_DESCRIPTOR = XmlParserOptions.EXPAND_ENTITY_REFERENCES_DESCRIPTOR; - @Deprecated - public static final PropertyDescriptor IGNORING_COMMENTS_DESCRIPTOR = XmlParserOptions.IGNORING_COMMENTS_DESCRIPTOR; - @Deprecated - public static final PropertyDescriptor IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR = XmlParserOptions.IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR; - @Deprecated - public static final PropertyDescriptor NAMESPACE_AWARE_DESCRIPTOR = XmlParserOptions.NAMESPACE_AWARE_DESCRIPTOR; - @Deprecated - public static final PropertyDescriptor VALIDATING_DESCRIPTOR = XmlParserOptions.VALIDATING_DESCRIPTOR; - @Deprecated - public static final PropertyDescriptor XINCLUDE_AWARE_DESCRIPTOR = XmlParserOptions.XINCLUDE_AWARE_DESCRIPTOR; - public AbstractXmlRule() { - super.setLanguage(LanguageRegistry.getLanguage(XmlLanguageModule.NAME)); - defineProperties(); - } - - protected AbstractXmlRule(Language language) { - super.setLanguage(language); - defineProperties(); - } - - private void defineProperties() { - definePropertyDescriptor(COALESCING_DESCRIPTOR); - definePropertyDescriptor(EXPAND_ENTITY_REFERENCES_DESCRIPTOR); - definePropertyDescriptor(IGNORING_COMMENTS_DESCRIPTOR); - definePropertyDescriptor(IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR); - definePropertyDescriptor(NAMESPACE_AWARE_DESCRIPTOR); - definePropertyDescriptor(VALIDATING_DESCRIPTOR); - definePropertyDescriptor(XINCLUDE_AWARE_DESCRIPTOR); - } - - @Override - public ParserOptions getParserOptions() { - return new XmlParserOptions(this); - } - - @Override - public void apply(Node target, RuleContext ctx) { - visit((XmlNode) target, ctx); - } - - protected void visit(XmlNode node, RuleContext ctx) { - final int numChildren = node.getNumChildren(); - for (int i = 0; i < numChildren; i++) { - XmlNode child = (XmlNode) node.getChild(i); - visit(child, ctx); - } - } } diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRuleTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRuleTest.java deleted file mode 100644 index 62578df6f3..0000000000 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/wsdl/rule/AbstractWsdlRuleTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.wsdl.rule; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Test; - -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.xml.XmlParsingHelper; -import net.sourceforge.pmd.lang.xml.ast.XmlNode; - -public class AbstractWsdlRuleTest { - - @Test - public void testVisit() throws Exception { - String source = ""; - XmlNode xmlNode = XmlParsingHelper.WSDL.parse(source); - - MyRule rule = new MyRule(); - rule.apply(xmlNode, null); - - assertEquals(3, rule.visitedNodes.size()); - assertEquals("document", rule.visitedNodes.get(0).toString()); - assertEquals("foo", rule.visitedNodes.get(1).toString()); - assertEquals("bar", rule.visitedNodes.get(2).toString()); - } - - private static class MyRule extends AbstractWsdlRule { - final List visitedNodes = new ArrayList<>(); - - MyRule() { - } - - @Override - public void start(RuleContext ctx) { - visitedNodes.clear(); - } - - @Override - protected void visit(XmlNode node, RuleContext ctx) { - visitedNodes.add(node); - super.visit(node, ctx); - } - } -} 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 deleted file mode 100644 index 547ea01bbe..0000000000 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserOptionsTest.java +++ /dev/null @@ -1,168 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xml; - -import static net.sourceforge.pmd.lang.ParserOptionsTestUtils.verifyOptionsEqualsHashcode; -import static net.sourceforge.pmd.util.CollectionUtil.listOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.junit.Test; - -import net.sourceforge.pmd.lang.ParserOptions; -import net.sourceforge.pmd.lang.xml.rule.AbstractXmlRule; -import net.sourceforge.pmd.properties.PropertyDescriptor; - -public class XmlParserOptionsTest { - - @Test - public void testDefaults() { - XmlParserOptions options = new XmlParserOptions(); - assertFalse(options.isCoalescing()); - assertTrue(options.isExpandEntityReferences()); - assertFalse(options.isIgnoringComments()); - assertFalse(options.isIgnoringElementContentWhitespace()); - assertTrue(options.isNamespaceAware()); - assertFalse(options.isValidating()); - assertFalse(options.isXincludeAware()); - - MyRule rule = new MyRule(); - options = (XmlParserOptions) rule.getParserOptions(); - assertFalse(options.isCoalescing()); - assertTrue(options.isExpandEntityReferences()); - assertFalse(options.isIgnoringComments()); - assertFalse(options.isIgnoringElementContentWhitespace()); - assertTrue(options.isNamespaceAware()); - assertFalse(options.isValidating()); - assertFalse(options.isXincludeAware()); - } - - @Test - public void testConstructor() { - MyRule rule = new MyRule(); - - rule.setProperty(XmlParserOptions.COALESCING_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isCoalescing()); - rule.setProperty(XmlParserOptions.COALESCING_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isCoalescing()); - - rule.setProperty(XmlParserOptions.EXPAND_ENTITY_REFERENCES_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isExpandEntityReferences()); - rule.setProperty(XmlParserOptions.EXPAND_ENTITY_REFERENCES_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isExpandEntityReferences()); - - rule.setProperty(XmlParserOptions.IGNORING_COMMENTS_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isIgnoringComments()); - rule.setProperty(XmlParserOptions.IGNORING_COMMENTS_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isIgnoringComments()); - - rule.setProperty(XmlParserOptions.IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isIgnoringElementContentWhitespace()); - rule.setProperty(XmlParserOptions.IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isIgnoringElementContentWhitespace()); - - rule.setProperty(XmlParserOptions.NAMESPACE_AWARE_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isNamespaceAware()); - rule.setProperty(XmlParserOptions.NAMESPACE_AWARE_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isNamespaceAware()); - - rule.setProperty(XmlParserOptions.VALIDATING_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isValidating()); - rule.setProperty(XmlParserOptions.VALIDATING_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isValidating()); - - rule.setProperty(XmlParserOptions.XINCLUDE_AWARE_DESCRIPTOR, true); - assertTrue(((XmlParserOptions) rule.getParserOptions()).isXincludeAware()); - rule.setProperty(XmlParserOptions.XINCLUDE_AWARE_DESCRIPTOR, false); - assertFalse(((XmlParserOptions) rule.getParserOptions()).isXincludeAware()); - } - - @Test - public void testSetters() { - XmlParserOptions options = new XmlParserOptions(); - - options.setSuppressMarker("foo"); - assertEquals("foo", options.getSuppressMarker()); - - options.setCoalescing(true); - assertTrue(options.isCoalescing()); - options.setCoalescing(false); - assertFalse(options.isCoalescing()); - - options.setExpandEntityReferences(true); - assertTrue(options.isExpandEntityReferences()); - options.setExpandEntityReferences(false); - assertFalse(options.isExpandEntityReferences()); - - options.setIgnoringComments(true); - assertTrue(options.isIgnoringComments()); - options.setIgnoringComments(false); - assertFalse(options.isIgnoringComments()); - - options.setIgnoringElementContentWhitespace(true); - assertTrue(options.isIgnoringElementContentWhitespace()); - options.setIgnoringElementContentWhitespace(false); - assertFalse(options.isIgnoringElementContentWhitespace()); - - options.setNamespaceAware(true); - assertTrue(options.isNamespaceAware()); - options.setNamespaceAware(false); - assertFalse(options.isNamespaceAware()); - - options.setValidating(true); - assertTrue(options.isValidating()); - options.setValidating(false); - assertFalse(options.isValidating()); - - options.setXincludeAware(true); - assertTrue(options.isXincludeAware()); - options.setXincludeAware(false); - assertFalse(options.isXincludeAware()); - } - - @Test - public void testEqualsHashcode() throws Exception { - List> properties = listOf(XmlParserOptions.COALESCING_DESCRIPTOR, - XmlParserOptions.EXPAND_ENTITY_REFERENCES_DESCRIPTOR, - XmlParserOptions.IGNORING_COMMENTS_DESCRIPTOR, - XmlParserOptions.IGNORING_ELEMENT_CONTENT_WHITESPACE_DESCRIPTOR, - XmlParserOptions.NAMESPACE_AWARE_DESCRIPTOR, - XmlParserOptions.VALIDATING_DESCRIPTOR, - XmlParserOptions.XINCLUDE_AWARE_DESCRIPTOR); - - for (PropertyDescriptor property : properties) { - MyRule rule = new MyRule(); - rule.setProperty(property, true); - ParserOptions options1 = rule.getParserOptions(); - rule.setProperty(property, false); - ParserOptions options2 = rule.getParserOptions(); - rule.setProperty(property, true); - ParserOptions options3 = rule.getParserOptions(); - rule.setProperty(property, false); - ParserOptions options4 = rule.getParserOptions(); - verifyOptionsEqualsHashcode(options1, options2, options3, options4); - } - - XmlParserOptions options1 = new XmlParserOptions(); - options1.setSuppressMarker("foo"); - XmlParserOptions options2 = new XmlParserOptions(); - options2.setSuppressMarker("bar"); - XmlParserOptions options3 = new XmlParserOptions(); - options3.setSuppressMarker("foo"); - XmlParserOptions options4 = new XmlParserOptions(); - options4.setSuppressMarker("bar"); - verifyOptionsEqualsHashcode(options1, options2, options3, options4); - } - - private static final class MyRule extends AbstractXmlRule { - } - - public static junit.framework.Test suite() { - return new junit.framework.JUnit4TestAdapter(XmlParserOptionsTest.class); - } -} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserTest.java deleted file mode 100644 index 591fad6ee9..0000000000 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/XmlParserTest.java +++ /dev/null @@ -1,431 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xml; - -import static net.sourceforge.pmd.lang.xml.XmlParsingHelper.XML; - -import java.io.ByteArrayOutputStream; -import java.io.PrintStream; -import java.io.UnsupportedEncodingException; -import java.util.Iterator; -import java.util.Locale; - -import org.junit.Assert; -import org.junit.Test; - -import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.rule.xpath.Attribute; -import net.sourceforge.pmd.lang.xml.ast.XmlNode; -import net.sourceforge.pmd.lang.xml.ast.internal.XmlParserImpl; -import net.sourceforge.pmd.util.StringUtil; - -/** - * Unit test for the {@link XmlParserImpl}. - */ -public class XmlParserTest { - - private static final String XML_TEST = "\n" + "\n" + "\n" - + "\n" + "\n" + "\n" - + "\n" + "]\n" + ">\n" + "\n" - + " \n" + " entity: &pmd;\n" + " \n" - + " \n" + " \n" + " \n" + ""; - - private static final String XML_NAMESPACE_TEST = "\n" - + "\n" + " \n" - + " entity: &\n" + " \n" + " \n" - + " \n" + " \n" + ""; - - private static final String XML_INVALID_WITH_DTD = "\n" + "\n" + "\n" + "]\n" + ">\n" + "\n" - + " \n" + ""; - - /** - * See bug #1054: XML Rules ever report a line -1 and not the line/column - * where the error occurs - * - */ - @Test - public void testLineNumbers() { - Node document = XML.parse(XML_TEST); - - assertNode(document, "document", 2); - assertLineNumbers(document, 1, 1, 19, 14); - Node dtdElement = document.getChild(0); - assertNode(dtdElement, "rootElement", 0); - assertLineNumbers(dtdElement, 2, 1, 11, 1); - Node rootElement = document.getChild(1); - assertNode(rootElement, "rootElement", 7); - assertLineNumbers(rootElement, 12, 1, 19, 14); - assertTextNode(rootElement.getChild(0), "\\n "); - assertLineNumbers(rootElement.getChild(0), 12, 14, 13, 4); - assertNode(rootElement.getChild(1), "comment", 0); - assertLineNumbers(rootElement.getChild(1), 13, 5, 13, 29); - assertTextNode(rootElement.getChild(2), "\\n "); - assertLineNumbers(rootElement.getChild(2), 13, 30, 14, 4); - Node child1 = rootElement.getChild(3); - assertNode(child1, "child1", 1, "test", "1"); - assertLineNumbers(child1, 14, 5, 15, 13); - assertTextNode(child1.getChild(0), "entity: Copyright: PMD\\n "); - assertLineNumbers(child1.getChild(0), 14, 22, 15, 4); - assertTextNode(rootElement.getChild(4), "\\n "); - assertLineNumbers(rootElement.getChild(4), 15, 14, 16, 4); - Node child2 = rootElement.getChild(5); - assertNode(child2, "child2", 3); - assertLineNumbers(child2, 16, 5, 18, 13); - assertTextNode(child2.getChild(0), "\\n "); - assertLineNumbers(child2.getChild(0), 16, 13, 17, 6); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertLineNumbers(child2.getChild(1), 17, 7, 17, 33); - assertTextNode(child2.getChild(2), "\\n "); - assertLineNumbers(child2.getChild(2), 17, 34, 18, 4); - assertTextNode(rootElement.getChild(6), "\\n"); - assertLineNumbers(rootElement.getChild(6), 18, 14, 18, 14); - } - - /** - * Verifies the default parsing behavior of the XML parser. - */ - @Test - public void testDefaultParsing() { - Node document = XML.parse(XML_TEST); - - assertNode(document, "document", 2); - Node dtdElement = document.getChild(0); - assertNode(dtdElement, "rootElement", 0); - Node rootElement = document.getChild(1); - assertNode(rootElement, "rootElement", 7); - assertTextNode(rootElement.getChild(0), "\\n "); - assertNode(rootElement.getChild(1), "comment", 0); - assertTextNode(rootElement.getChild(2), "\\n "); - Node child1 = rootElement.getChild(3); - assertNode(child1, "child1", 1, "test", "1"); - assertTextNode(child1.getChild(0), "entity: Copyright: PMD\\n "); - assertTextNode(rootElement.getChild(4), "\\n "); - Node child2 = rootElement.getChild(5); - assertNode(child2, "child2", 3); - assertTextNode(child2.getChild(0), "\\n "); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertTextNode(child2.getChild(2), "\\n "); - assertTextNode(rootElement.getChild(6), "\\n"); - } - - /** - * Verifies the parsing behavior of the XML parser with coalescing enabled. - */ - @Test - public void testParsingCoalescingEnabled() { - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setCoalescing(true); - Node document = XML.withParserOptions(parserOptions).parse(XML_TEST); - - assertNode(document, "document", 2); - Node dtdElement = document.getChild(0); - assertNode(dtdElement, "rootElement", 0); - Node rootElement = document.getChild(1); - assertNode(rootElement, "rootElement", 7); - assertTextNode(rootElement.getChild(0), "\\n "); - assertNode(rootElement.getChild(1), "comment", 0); - assertTextNode(rootElement.getChild(2), "\\n "); - Node child1 = rootElement.getChild(3); - assertNode(child1, "child1", 1, "test", "1"); - assertTextNode(child1.getChild(0), "entity: Copyright: PMD\\n "); - assertTextNode(rootElement.getChild(4), "\\n "); - Node child2 = rootElement.getChild(5); - assertNode(child2, "child2", 1); - assertTextNode(child2.getChild(0), "\\n cdata section \\n "); - assertTextNode(rootElement.getChild(6), "\\n"); - } - - /** - * Verifies the parsing behavior of the XML parser if entities are not - * expanded. - */ - @Test - public void testParsingDoNotExpandEntities() { - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setExpandEntityReferences(false); - Node document = XML.withParserOptions(parserOptions).parse(XML_TEST); - - assertNode(document, "document", 2); - Node dtdElement = document.getChild(0); - assertNode(dtdElement, "rootElement", 0); - Node rootElement = document.getChild(1); - assertNode(rootElement, "rootElement", 7); - assertTextNode(rootElement.getChild(0), "\\n "); - assertNode(rootElement.getChild(1), "comment", 0); - assertTextNode(rootElement.getChild(2), "\\n "); - Node child1 = rootElement.getChild(3); - assertNode(child1, "child1", 3, "test", "1"); - assertTextNode(child1.getChild(0), "entity: "); - assertNode(child1.getChild(1), "pmd", 0); - // with java13, expandEntityReferences=false works correctly, and the - // entity &pmd; is not expanded - String text = child1.getChild(2).getImage(); - if ("\n ".equals(text)) { - // java13 and later - assertTextNode(child1.getChild(2), "\\n "); - } else { - assertTextNode(child1.getChild(2), "Copyright: PMD\\n "); - } - assertTextNode(rootElement.getChild(4), "\\n "); - Node child2 = rootElement.getChild(5); - assertNode(child2, "child2", 3); - assertTextNode(child2.getChild(0), "\\n "); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertTextNode(child2.getChild(2), "\\n "); - assertTextNode(rootElement.getChild(6), "\\n"); - } - - /** - * Verifies the parsing behavior of the XML parser if ignoring comments. - */ - @Test - public void testParsingIgnoreComments() { - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setIgnoringComments(true); - Node document = XML.withParserOptions(parserOptions).parse(XML_TEST); - - assertNode(document, "document", 2); - Node dtdElement = document.getChild(0); - assertNode(dtdElement, "rootElement", 0); - Node rootElement = document.getChild(1); - assertNode(rootElement, "rootElement", 5); - assertTextNode(rootElement.getChild(0), "\\n \\n "); - Node child1 = rootElement.getChild(1); - assertNode(child1, "child1", 1, "test", "1"); - assertTextNode(child1.getChild(0), "entity: Copyright: PMD\\n "); - assertTextNode(rootElement.getChild(2), "\\n "); - Node child2 = rootElement.getChild(3); - assertNode(child2, "child2", 3); - assertTextNode(child2.getChild(0), "\\n "); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertTextNode(child2.getChild(2), "\\n "); - assertTextNode(rootElement.getChild(4), "\\n"); - } - - /** - * Verifies the parsing behavior of the XML parser if ignoring whitespaces - * in elements. - */ - @Test - public void testParsingIgnoreElementContentWhitespace() { - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setIgnoringElementContentWhitespace(true); - Node document = XML.withParserOptions(parserOptions).parse(XML_TEST); - - assertNode(document, "document", 2); - Node dtdElement = document.getChild(0); - assertNode(dtdElement, "rootElement", 0); - Node rootElement = document.getChild(1); - assertNode(rootElement, "rootElement", 3); - assertNode(rootElement.getChild(0), "comment", 0); - Node child1 = rootElement.getChild(1); - assertNode(child1, "child1", 1, "test", "1"); - assertTextNode(child1.getChild(0), "entity: Copyright: PMD\\n "); - Node child2 = rootElement.getChild(2); - assertNode(child2, "child2", 3); - assertTextNode(child2.getChild(0), "\\n "); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertTextNode(child2.getChild(2), "\\n "); - } - - /** - * Verifies the default parsing behavior of the XML parser with namespaces. - */ - @Test - public void testDefaultParsingNamespaces() { - Node document = XML.parse(XML_NAMESPACE_TEST); - - assertNode(document, "document", 1); - Node rootElement = document.getChild(0); - assertNode(rootElement, "pmd:rootElement", 7, "xmlns:pmd", "http://pmd.sf.net"); - Assert.assertEquals("http://pmd.sf.net", ((XmlNode) rootElement).getNode().getNamespaceURI()); - Assert.assertEquals("pmd", ((XmlNode) rootElement).getNode().getPrefix()); - Assert.assertEquals("rootElement", ((XmlNode) rootElement).getNode().getLocalName()); - Assert.assertEquals("pmd:rootElement", ((XmlNode) rootElement).getNode().getNodeName()); - assertTextNode(rootElement.getChild(0), "\\n "); - assertNode(rootElement.getChild(1), "comment", 0); - assertTextNode(rootElement.getChild(2), "\\n "); - Node child1 = rootElement.getChild(3); - assertNode(child1, "pmd:child1", 1, "test", "1"); - assertTextNode(child1.getChild(0), "entity: &\\n "); - assertTextNode(rootElement.getChild(4), "\\n "); - Node child2 = rootElement.getChild(5); - assertNode(child2, "pmd:child2", 3); - assertTextNode(child2.getChild(0), "\\n "); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertTextNode(child2.getChild(2), "\\n "); - assertTextNode(rootElement.getChild(6), "\\n"); - } - - /** - * Verifies the default parsing behavior of the XML parser with namespaces - * but not namespace aware. - */ - @Test - public void testParsingNotNamespaceAware() { - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setNamespaceAware(false); - Node document = XML.withParserOptions(parserOptions).parse(XML_NAMESPACE_TEST); - - assertNode(document, "document", 1); - Node rootElement = document.getChild(0); - assertNode(rootElement, "pmd:rootElement", 7, "xmlns:pmd", "http://pmd.sf.net"); - Assert.assertNull(((XmlNode) rootElement).getNode().getNamespaceURI()); - Assert.assertNull(((XmlNode) rootElement).getNode().getPrefix()); - Assert.assertNull(((XmlNode) rootElement).getNode().getLocalName()); - Assert.assertEquals("pmd:rootElement", ((XmlNode) rootElement).getNode().getNodeName()); - assertTextNode(rootElement.getChild(0), "\\n "); - assertNode(rootElement.getChild(1), "comment", 0); - assertTextNode(rootElement.getChild(2), "\\n "); - Node child1 = rootElement.getChild(3); - assertNode(child1, "pmd:child1", 1, "test", "1"); - assertTextNode(child1.getChild(0), "entity: &\\n "); - assertTextNode(rootElement.getChild(4), "\\n "); - Node child2 = rootElement.getChild(5); - assertNode(child2, "pmd:child2", 3); - assertTextNode(child2.getChild(0), "\\n "); - assertTextNode(child2.getChild(1), " cdata section ", "cdata-section"); - assertTextNode(child2.getChild(2), "\\n "); - assertTextNode(rootElement.getChild(6), "\\n"); - } - - /** - * Verifies the parsing behavior of the XML parser with validation on. - * - * @throws UnsupportedEncodingException - * error - */ - @Test - public void testParsingWithValidation() throws UnsupportedEncodingException { - XmlParserOptions parserOptions = new XmlParserOptions(); - parserOptions.setValidating(true); - PrintStream oldErr = System.err; - Locale oldLocale = Locale.getDefault(); - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - System.setErr(new PrintStream(bos)); - Locale.setDefault(Locale.ENGLISH); - Node document = XML.withParserOptions(parserOptions).parse(XML_INVALID_WITH_DTD); - Assert.assertNotNull(document); - String output = bos.toString("UTF-8"); - Assert.assertTrue(output.contains("Element type \"invalidChild\" must be declared.")); - Assert.assertTrue(output.contains("The content of element type \"rootElement\" must match \"(child)\".")); - Assert.assertEquals(2, document.getNumChildren()); - Assert.assertEquals("invalidChild", String.valueOf(document.getChild(1).getChild(1))); - } finally { - System.setErr(oldErr); - Locale.setDefault(oldLocale); - } - } - - @Test - public void testWithProcessingInstructions() { - String xml = "]>TEXT>&myentity;<"; - XmlParserOptions options = new XmlParserOptions(); - options.setExpandEntityReferences(false); - Node document = XmlParsingHelper.XML.withParserOptions(options).parse(xml); - Assert.assertNotNull(document); - assertNode(document.getChild(0), "mypi", 0); - assertLineNumbers(document.getChild(0), 1, 22, 1, 29); - } - - @Test - public void testBug1518() throws Exception { - XML.parseResource("parsertests/bug1518.xml"); - } - - @Test - public void testAutoclosingElementLength() { - final String xml = ""; - assertLineNumbers(XML.parse(xml), 1, 1, 1, xml.length()); - } - - /** - * Asserts a single node inclusive attributes. - * - * @param node - * the node - * @param toString - * the to String representation to expect - * @param childs - * number of childs - * @param atts - * attributes - each object pair forms one attribute: first name, - * then value. - */ - private void assertNode(Node node, String toString, int childs, Object... atts) { - Assert.assertEquals(toString, String.valueOf(node)); - Assert.assertEquals(childs, node.getNumChildren()); - Iterator attributeIterator = node.getXPathAttributesIterator(); - if (atts != null) { - for (int i = 0; i < atts.length; i += 2) { - Assert.assertTrue(attributeIterator.hasNext()); - String name = String.valueOf(atts[i]); - Object value = atts[i + 1]; - Attribute attribute = attributeIterator.next(); - Assert.assertEquals(name, attribute.getName()); - Assert.assertEquals(value, attribute.getValue()); - } - } - Assert.assertFalse(attributeIterator.hasNext()); - } - - /** - * Assert a single text node. - * - * @param node - * the node to check - * @param text - * the text to expect - */ - private void assertTextNode(Node node, String text) { - assertTextNode(node, text, "text"); - } - - /** - * Assert a single text node. - * - * @param node - * the node to check - * @param text - * the text to expect - * @param toString - * the to string representation - */ - private void assertTextNode(Node node, String text, String toString) { - Assert.assertEquals(toString, String.valueOf(node)); - Assert.assertEquals(0, node.getNumChildren()); - Assert.assertEquals(text, StringUtil.escapeWhitespace(node.getImage())); - Iterator attributeIterator = node.getXPathAttributesIterator(); - Assert.assertTrue(attributeIterator.hasNext()); - Attribute attribute = attributeIterator.next(); - Assert.assertEquals("Image", attribute.getName()); - Assert.assertEquals(text, StringUtil.escapeWhitespace(attribute.getValue())); - Assert.assertFalse(attributeIterator.hasNext()); - } - - /** - * Assert the line numbers of a node. - * - * @param node - * the node - * @param beginLine - * the begin line - * @param beginColumn - * the begin column - * @param endLine - * the end line - * @param endColumn - * the end column - */ - private void assertLineNumbers(Node node, int beginLine, int beginColumn, int endLine, int endColumn) { - Assert.assertEquals("begin line wrong", beginLine, node.getBeginLine()); - Assert.assertEquals("begin column wrong", beginColumn, node.getBeginColumn()); - Assert.assertEquals("end line wrong", endLine, node.getEndLine()); - Assert.assertEquals("end column wrong", endColumn, node.getEndColumn()); - } -} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlCoordinatesTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlCoordinatesTest.java new file mode 100644 index 0000000000..de63fad6a8 --- /dev/null +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlCoordinatesTest.java @@ -0,0 +1,58 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.xml.ast; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; +import net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest; +import net.sourceforge.pmd.lang.ast.test.CoordinatesPrinter; +import net.sourceforge.pmd.lang.xml.XmlParsingHelper; + +public class XmlCoordinatesTest extends BaseTreeDumpTest { + + public XmlCoordinatesTest() { + super(CoordinatesPrinter.INSTANCE, ".xml"); + } + + @Override + public @NonNull BaseParsingHelper getParser() { + return XmlParsingHelper.XML.withResourceContext(getClass(), "testdata"); + } + + /** + * See bug #1054: XML Rules ever report a line -1 and not the line/column + * where the error occurs + */ + @Test + public void testLineNumbers() { + doTest("xmlCoords"); + } + + @Test + public void testAutoclosingElementLength() { + final String xml = ""; + assertLineNumbers(XmlParsingHelper.XML.parse(xml), 1, 1, 1, xml.length()); + } + + /** + * Assert the line numbers of a node. + * + * @param node the node + * @param beginLine the begin line + * @param beginColumn the begin column + * @param endLine the end line + * @param endColumn the end column + */ + private void assertLineNumbers(Node node, int beginLine, int beginColumn, int endLine, int endColumn) { + Assert.assertEquals("begin line wrong", beginLine, node.getBeginLine()); + Assert.assertEquals("begin column wrong", beginColumn, node.getBeginColumn()); + Assert.assertEquals("end line wrong", endLine, node.getEndLine()); + Assert.assertEquals("end column wrong", endColumn, node.getEndColumn()); + } +} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlParserTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlParserTest.java new file mode 100644 index 0000000000..c2dc80c646 --- /dev/null +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlParserTest.java @@ -0,0 +1,43 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.xml.ast; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.junit.Test; + +import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; +import net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest; +import net.sourceforge.pmd.lang.ast.test.RelevantAttributePrinter; +import net.sourceforge.pmd.lang.xml.XmlParsingHelper; + +public class XmlParserTest extends BaseTreeDumpTest { + + public XmlParserTest() { + super(new RelevantAttributePrinter(), ".xml"); + } + + @Override + public @NonNull BaseParsingHelper getParser() { + return XmlParsingHelper.XML.withResourceContext(getClass(), "testdata"); + } + + /** + * Verifies the default parsing behavior of the XML parser. + */ + @Test + public void testDefaultParsing() { + doTest("sampleXml"); + } + + @Test + public void testNamespaces() { + doTest("sampleNs"); + } + + @Test + public void testBug1518() { + doTest("bug1518"); + } +} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRuleTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRuleTest.java deleted file mode 100644 index f8d7d63016..0000000000 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/rule/AbstractXmlRuleTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang.xml.rule; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Test; - -import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.xml.XmlParsingHelper; -import net.sourceforge.pmd.lang.xml.ast.XmlNode; - -public class AbstractXmlRuleTest { - - @Test - public void testVisit() throws Exception { - String source = ""; - XmlNode xmlNode = XmlParsingHelper.XML.parse(source); - MyRule rule = new MyRule(); - rule.apply(xmlNode, null); - - assertEquals(3, rule.visitedNodes.size()); - assertEquals("document", rule.visitedNodes.get(0).toString()); - assertEquals("foo", rule.visitedNodes.get(1).toString()); - assertEquals("bar", rule.visitedNodes.get(2).toString()); - } - - private static class MyRule extends AbstractXmlRule { - final List visitedNodes = new ArrayList<>(); - - MyRule() { - } - - @Override - public void start(RuleContext ctx) { - visitedNodes.clear(); - } - - @Override - protected void visit(XmlNode node, RuleContext ctx) { - visitedNodes.add(node); - super.visit(node, ctx); - } - } -} diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.txt new file mode 100644 index 0000000000..238556b80b --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.txt @@ -0,0 +1,69 @@ ++- document[] + +- deployment-plan[@global-variables = "false", @xmlns = "http://xmlns.oracle.com/weblogic/deployment-plan", @xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance", @xsi:schemaLocation = "http://xmlns.oracle.com/weblogic/deployment-plan http://xmlns.oracle.com/weblogic/deployment-plan/1.0/deployment-plan.xsd"] + +- text[@Image = " + "] + +- application-name[] + | +- text[@Image = "app"] + +- text[@Image = " + "] + +- variable-definition[] + | +- text[@Image = " + "] + | +- variable[] + | | +- text[@Image = " + "] + | | +- name[] + | | | +- text[@Image = "Application_Module_Web_ContextRoot"] + | | +- text[@Image = " + "] + | | +- value[@xsi:nil = "false"] + | | | +- text[@Image = "ikb.adf.kreda.abw.webapp-Abnahmetest-ohne-SSO"] + | | +- text[@Image = " + "] + | +- text[@Image = " + "] + +- text[@Image = " + "] + +- module-override[] + | +- text[@Image = " + "] + | +- module-name[] + | | +- text[@Image = "ikb.adf.kreda.abw.ear"] + | +- text[@Image = " + "] + | +- module-type[] + | | +- text[@Image = "ear"] + | +- text[@Image = " + "] + | +- module-descriptor[@external = "false"] + | | +- text[@Image = " + "] + | | +- root-element[] + | | | +- text[@Image = "application"] + | | +- text[@Image = " + "] + | | +- uri[] + | | | +- text[@Image = "META-INF/application.xml"] + | | +- text[@Image = " + "] + | | +- variable-assignment[] + | | | +- text[@Image = " + "] + | | | +- name[] + | | | | +- text[@Image = "Application_Module_Web_ContextRoot"] + | | | +- text[@Image = " + "] + | | | +- xpath[] + | | | | +- text[@Image = "/application/module/web/[context-root="ikb.adf.kreda.abw.webapp-Local-ohne-SSO"]"] + | | | +- text[@Image = " + "] + | | | +- operation[] + | | | | +- text[@Image = "replace"] + | | | +- text[@Image = " + "] + | | +- text[@Image = " + "] + | +- text[@Image = " + "] + +- text[@Image = " +"] diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/parsertests/bug1518.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.xml similarity index 100% rename from pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/parsertests/bug1518.xml rename to pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/bug1518.xml diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.txt new file mode 100644 index 0000000000..14d8a5cf04 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.txt @@ -0,0 +1,22 @@ ++- document[] + +- comment[] + +- rootElement[] + +- rootElement[] + +- text[@Image = " + "] + +- comment[] + +- text[@Image = " + "] + +- child1[@test = "1"] + | +- text[@Image = "entity: Copyright: PMD + "] + +- text[@Image = " + "] + +- child2[] + | +- text[@Image = " + "] + | +- cdata-section[@Image = " cdata section "] + | +- text[@Image = " + "] + +- text[@Image = " +"] diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.xml new file mode 100644 index 0000000000..3d242f0de8 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.xml @@ -0,0 +1,21 @@ + + + + + + + + + + ] + > + + + entity: &pmd; + + + + + diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.txt new file mode 100644 index 0000000000..967dc4b8a5 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.txt @@ -0,0 +1,20 @@ ++- document[] + +- pmd:rootElement[@xmlns:pmd = "http://pmd.sf.net"] + +- text[@Image = " + "] + +- comment[] + +- text[@Image = " + "] + +- pmd:child1[@test = "1"] + | +- text[@Image = "entity: & + "] + +- text[@Image = " + "] + +- pmd:child2[] + | +- text[@Image = " + "] + | +- cdata-section[@Image = " cdata section "] + | +- text[@Image = " + "] + +- text[@Image = " +"] diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.xml new file mode 100644 index 0000000000..6e0e76f927 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleXml.xml @@ -0,0 +1,9 @@ + + + + entity: & + + + + + diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.txt b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.txt new file mode 100644 index 0000000000..c946430434 --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.txt @@ -0,0 +1,17 @@ ++- document[@BeginColumn = 1, @BeginLine = 1, @EndColumn = 14, @EndLine = 22] + +- comment[@BeginColumn = 1, @BeginLine = 2, @EndColumn = 12, @EndLine = 2] + +- rootElement[@BeginColumn = 1, @BeginLine = 4, @EndColumn = 9, @EndLine = 13] + +- rootElement[@BeginColumn = 1, @BeginLine = 14, @EndColumn = 14, @EndLine = 22] + +- text[@BeginColumn = 14, @BeginLine = 14, @EndColumn = 4, @EndLine = 15] + +- comment[@BeginColumn = 5, @BeginLine = 15, @EndColumn = 29, @EndLine = 15] + +- text[@BeginColumn = 30, @BeginLine = 15, @EndColumn = 4, @EndLine = 16] + +- child1[@BeginColumn = 5, @BeginLine = 16, @EndColumn = 13, @EndLine = 17] + | +- text[@BeginColumn = 22, @BeginLine = 16, @EndColumn = 4, @EndLine = 17] + +- text[@BeginColumn = 14, @BeginLine = 17, @EndColumn = 4, @EndLine = 18] + +- child2[@BeginColumn = 5, @BeginLine = 18, @EndColumn = 13, @EndLine = 20] + | +- text[@BeginColumn = 13, @BeginLine = 18, @EndColumn = 8, @EndLine = 19] + | +- cdata-section[@BeginColumn = 9, @BeginLine = 19, @EndColumn = 35, @EndLine = 19] + | +- text[@BeginColumn = 36, @BeginLine = 19, @EndColumn = 4, @EndLine = 20] + +- text[@BeginColumn = 14, @BeginLine = 20, @EndColumn = 4, @EndLine = 21] + +- child3[@BeginColumn = 5, @BeginLine = 21, @EndColumn = 20, @EndLine = 21] + +- text[@BeginColumn = 21, @BeginLine = 21, @EndColumn = 21, @EndLine = 21] diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.xml new file mode 100644 index 0000000000..a3ed9805ee --- /dev/null +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/xmlCoords.xml @@ -0,0 +1,22 @@ + + + + + + + + + + ] + > + + + entity: &pmd; + + + + + + From 084a81265c5ba8138aaa2d3cec15e93e37ec39bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 12 Dec 2020 23:28:51 +0100 Subject: [PATCH 294/299] Cleanup tests --- .../pmd/lang/apex/ast/ApexParserTest.java | 10 +----- .../pmd/lang/java/ast/SimpleNodeTest.java | 20 ++++-------- .../pmd/lang/ast/test/TestUtils.kt | 9 ++++++ .../pmd/lang/scala/ast/ScalaTreeTests.kt | 32 +++++++------------ .../pmd/lang/xml/ast/XmlCoordinatesTest.java | 21 ++---------- 5 files changed, 32 insertions(+), 60 deletions(-) diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java index 34982e3b91..bebc2eacde 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserTest.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.apex.ast; +import static net.sourceforge.pmd.lang.ast.test.TestUtilsKt.assertPosition; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.junit.Assert.assertEquals; @@ -194,13 +195,4 @@ public class ApexParserTest extends ApexParserTestBase { } return result; } - - // TEST HELPER - - private static void assertPosition(Node node, int beginLine, int beginColumn, int endLine, int endColumn) { - assertEquals("Wrong begin line", beginLine, node.getBeginLine()); - assertEquals("Wrong begin column", beginColumn, node.getBeginColumn()); - assertEquals("Wrong end line", endLine, node.getEndLine()); - assertEquals("Wrong end column", endColumn, node.getEndColumn()); - } } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/SimpleNodeTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/SimpleNodeTest.java index 2d1dc1cb61..08077df94a 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/SimpleNodeTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ast/SimpleNodeTest.java @@ -16,6 +16,7 @@ import org.junit.Ignore; import org.junit.Test; import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.test.TestUtilsKt; import net.sourceforge.pmd.lang.java.JavaParsingHelper; @Ignore("This test is Java specific even though parts of it should apply to any language implementation") @@ -26,19 +27,19 @@ public class SimpleNodeTest extends BaseParserTest { @Test public void testMethodDiffLines() { List methods = java.getNodes(ASTMethodDeclaration.class, METHOD_DIFF_LINES); - verifyNode(methods.iterator().next(), 2, 9, 4, 3); + TestUtilsKt.assertPosition(methods.iterator().next(), 2, 9, 4, 3); } @Test public void testMethodSameLine() { List methods = java.getNodes(ASTMethodDeclaration.class, METHOD_SAME_LINE); - verifyNode(methods.iterator().next(), 2, 9, 2, 22); + TestUtilsKt.assertPosition(methods.iterator().next(), 2, 9, 2, 22); } @Test public void testNoLookahead() { List uCD = java.getNodes(ASTClassOrInterfaceDeclaration.class, NO_LOOKAHEAD); - verifyNode(uCD.iterator().next(), 1, 8, 1, 21); + TestUtilsKt.assertPosition(uCD.iterator().next(), 1, 8, 1, 21); } @Test @@ -69,7 +70,7 @@ public class SimpleNodeTest extends BaseParserTest { public void testColumnsOnQualifiedName() { for (Node node : java.getNodes(ASTName.class, QUALIFIED_NAME)) { if (node.getImage().equals("java.io.File")) { - verifyNode(node, 1, 8, 1, 20); + TestUtilsKt.assertPosition(node, 1, 8, 1, 20); } } } @@ -78,10 +79,10 @@ public class SimpleNodeTest extends BaseParserTest { public void testLineNumbersForNameSplitOverTwoLines() { for (Node node : java.getNodes(ASTName.class, BROKEN_LINE_IN_NAME)) { if (node.getImage().equals("java.io.File")) { - verifyNode(node, 1, 8, 2, 5); + TestUtilsKt.assertPosition(node, 1, 8, 2, 5); } if (node.getImage().equals("Foo")) { - verifyNode(node, 2, 15, 2, 19); + TestUtilsKt.assertPosition(node, 2, 15, 2, 19); } } } @@ -256,13 +257,6 @@ public class SimpleNodeTest extends BaseParserTest { assertTrue(nodes.get(0) instanceof ASTFieldDeclaration); } - private void verifyNode(Node node, int beginLine, int beginCol, int endLine, int endCol) { - assertEquals("Unexpected beginning line: ", beginLine, node.getBeginLine()); - assertEquals("Unexpected beginning column: ", beginCol, node.getBeginColumn()); - assertEquals("Unexpected ending line:", endLine, node.getEndLine()); - assertEquals("Unexpected ending column:", endCol, node.getEndColumn()); - } - private static final String HAS_EXPLICIT_EXTENDS = "public class Test extends Foo {}"; private static final String NO_EXPLICIT_EXTENDS = "public class Test {}"; diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt index caaca5ca62..932fa04b1e 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/TestUtils.kt @@ -9,6 +9,7 @@ import io.kotest.matchers.equalityMatcher import io.kotest.matchers.should import net.sourceforge.pmd.Report import net.sourceforge.pmd.RuleViolation +import net.sourceforge.pmd.lang.ast.Node import kotlin.reflect.KCallable import kotlin.reflect.jvm.isAccessible import kotlin.test.assertEquals @@ -91,3 +92,11 @@ fun assertSuppressed(report: Report, size: Int): List { child { - it.assertBounds(bline = 1, bcol = 1, eline = 3, ecol = 2) + it.assertPosition(bline = 1, bcol = 1, eline = 3, ecol = 2) it::isImplicit shouldBe false child { - it.assertBounds(bline = 1, bcol = 7, eline = 1, ecol = 10) + it.assertPosition(bline = 1, bcol = 7, eline = 1, ecol = 10) it::isImplicit shouldBe false } child { - it.assertBounds(bline = 1, bcol = 11, eline = 1, ecol = 11) // node has zero length + it.assertPosition(bline = 1, bcol = 11, eline = 1, ecol = 11) // node has zero length it::isImplicit shouldBe true child { - it.assertBounds(bline = 1, bcol = 11, eline = 1, ecol = 11) // node has zero length + it.assertPosition(bline = 1, bcol = 11, eline = 1, ecol = 11) // node has zero length it::isImplicit shouldBe true } } child { - it.assertBounds(bline = 1, bcol = 11, eline = 3, ecol = 2) + it.assertPosition(bline = 1, bcol = 11, eline = 3, ecol = 2) it::isImplicit shouldBe false child { - it.assertBounds(bline = 2, bcol = 2, eline = 2, ecol = 2) // node has zero length + it.assertPosition(bline = 2, bcol = 2, eline = 2, ecol = 2) // node has zero length it::isImplicit shouldBe true child { - it.assertBounds(bline = 2, bcol = 2, eline = 2, ecol = 2) // node has zero length + it.assertPosition(bline = 2, bcol = 2, eline = 2, ecol = 2) // node has zero length it::isImplicit shouldBe true } } child { - it.assertBounds(bline = 2, bcol = 2, eline = 2, ecol = 12) + it.assertPosition(bline = 2, bcol = 2, eline = 2, ecol = 12) it::isImplicit shouldBe false child { - it.assertBounds(bline = 2, bcol = 6, eline = 2, ecol = 7) + it.assertPosition(bline = 2, bcol = 6, eline = 2, ecol = 7) it::isImplicit shouldBe false child { - it.assertBounds(bline = 2, bcol = 6, eline = 2, ecol = 7) + it.assertPosition(bline = 2, bcol = 6, eline = 2, ecol = 7) it::isImplicit shouldBe false } } child { - it.assertBounds(bline = 2, bcol = 10, eline = 2, ecol = 12) + it.assertPosition(bline = 2, bcol = 10, eline = 2, ecol = 12) } } } @@ -80,10 +79,3 @@ class Foo { }) fun String.parseScala(): ASTSource = ScalaParsingHelper.DEFAULT.parse(this) - -fun Node.assertBounds(bline: Int, bcol: Int, eline: Int, ecol: Int) { - this::getBeginLine shouldBe bline - this::getBeginColumn shouldBe bcol - this::getEndLine shouldBe eline - this::getEndColumn shouldBe ecol -} diff --git a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlCoordinatesTest.java b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlCoordinatesTest.java index de63fad6a8..9a7ce14dba 100644 --- a/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlCoordinatesTest.java +++ b/pmd-xml/src/test/java/net/sourceforge/pmd/lang/xml/ast/XmlCoordinatesTest.java @@ -4,11 +4,11 @@ package net.sourceforge.pmd.lang.xml.ast; +import static net.sourceforge.pmd.lang.ast.test.TestUtilsKt.assertPosition; + import org.checkerframework.checker.nullness.qual.NonNull; -import org.junit.Assert; import org.junit.Test; -import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; import net.sourceforge.pmd.lang.ast.test.BaseTreeDumpTest; import net.sourceforge.pmd.lang.ast.test.CoordinatesPrinter; @@ -37,22 +37,7 @@ public class XmlCoordinatesTest extends BaseTreeDumpTest { @Test public void testAutoclosingElementLength() { final String xml = ""; - assertLineNumbers(XmlParsingHelper.XML.parse(xml), 1, 1, 1, xml.length()); + assertPosition(XmlParsingHelper.XML.parse(xml), 1, 1, 1, xml.length()); } - /** - * Assert the line numbers of a node. - * - * @param node the node - * @param beginLine the begin line - * @param beginColumn the begin column - * @param endLine the end line - * @param endColumn the end column - */ - private void assertLineNumbers(Node node, int beginLine, int beginColumn, int endLine, int endColumn) { - Assert.assertEquals("begin line wrong", beginLine, node.getBeginLine()); - Assert.assertEquals("begin column wrong", beginColumn, node.getBeginColumn()); - Assert.assertEquals("end line wrong", endLine, node.getEndLine()); - Assert.assertEquals("end column wrong", endColumn, node.getEndColumn()); - } } From 2623efbc8a72531d08256ccf774bdb8b25e4b94b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 13 Dec 2020 00:26:41 +0100 Subject: [PATCH 295/299] Delete ParserOptions --- .../pmd/lang/apex/ApexHandler.java | 3 +- .../main/java/net/sourceforge/pmd/Rule.java | 17 --- .../pmd/lang/LanguageVersionHandler.java | 15 +-- .../sourceforge/pmd/lang/ParserOptions.java | 107 ------------------ .../net/sourceforge/pmd/lang/ast/Parser.java | 5 - .../pmd/lang/rule/AbstractDelegateRule.java | 6 - .../pmd/lang/rule/AbstractRule.java | 13 --- .../pmd/lang/DummyLanguageModule.java | 4 +- .../java/internal/JavaLanguageHandler.java | 3 +- .../lang/ecmascript/EcmascriptHandler.java | 3 +- .../sourceforge/pmd/lang/jsp/JspHandler.java | 3 +- .../pmd/lang/ast/test/BaseParsingHelper.kt | 21 ++-- .../pmd/lang/modelica/ModelicaHandler.java | 3 +- .../pmd/lang/plsql/PLSQLHandler.java | 3 +- .../pmd/lang/scala/ScalaLanguageHandler.java | 3 +- .../pmd/lang/swift/SwiftHandler.java | 3 +- .../pmd/lang/ParserOptionsTest.java | 31 ----- .../pmd/lang/ParserOptionsTestUtils.java | 65 ----------- .../pmd/test/lang/DummyLanguageModule.java | 3 +- .../sourceforge/pmd/lang/vf/VfHandler.java | 3 +- .../lang/vf/ast/ApexClassPropertyTypes.java | 53 +++++---- .../vf/ast/ApexClassPropertyTypesVisitor.java | 9 +- .../ApexClassPropertyTypesVisitorTest.java | 24 ++-- .../vf/ast/VfExpressionTypeVisitorTest.java | 29 ++--- .../vf/rule/security/VfUnescapeElTest.java | 56 ++++----- .../sourceforge/pmd/lang/vm/VmHandler.java | 3 +- .../sourceforge/pmd/lang/xml/XmlHandler.java | 3 +- 27 files changed, 113 insertions(+), 378 deletions(-) delete mode 100644 pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java delete mode 100644 pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java delete mode 100644 pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTestUtils.java diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java index 285d0e295c..7c7879df5e 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexHandler.java @@ -9,7 +9,6 @@ import java.util.List; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.apex.ast.ASTMethod; import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface; import net.sourceforge.pmd.lang.apex.ast.ApexParser; @@ -33,7 +32,7 @@ public class ApexHandler extends AbstractPmdLanguageVersionHandler { } @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return new ApexParser(); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java index af609e581c..0b7d610fe8 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java @@ -9,10 +9,8 @@ import java.util.List; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.AstProcessingStage; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.rule.RuleTargetSelector; import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.properties.StringProperty; @@ -252,21 +250,6 @@ public interface Rule extends PropertySource { */ void setPriority(RulePriority priority); - /** - * Get the parser options for this Rule. Parser options are used to - * configure the {@link Parser} to create an AST in - * the form the Rule is expecting. Because ParserOptions are mutable, a Rule - * should return a new instance on each call. - * - * @return the parser options - * - * @deprecated This was never implemented and will never be. PMD - * cannot parse files once per rule. Let this method assume - * its default by not overriding it. - */ - @Deprecated - ParserOptions getParserOptions(); - /** * Returns true if this rule depends on the given processing stage diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java index d353230e77..dfc70d4508 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java @@ -49,14 +49,9 @@ public interface LanguageVersionHandler { /** - * Get the default ParserOptions. - * - * @return ParserOptions + * @deprecated This is transitional */ - default ParserOptions getDefaultParserOptions() { - return new ParserOptions(); - } - + @Deprecated default void declareParserTaskProperties(PropertySource source) { // do nothing } @@ -67,13 +62,9 @@ public interface LanguageVersionHandler { * * @return Parser */ - Parser getParser(ParserOptions parserOptions); + Parser getParser(); - default Parser getParser() { - return getParser(getDefaultParserOptions()); - } - /** * Get the RuleViolationFactory. 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 deleted file mode 100644 index 5028589bc0..0000000000 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ParserOptions.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang; - -import java.util.Objects; - -import net.sourceforge.pmd.PMD; -import net.sourceforge.pmd.lang.ast.Parser; -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 { - private String suppressMarker = PMD.SUPPRESS_MARKER; - - /** - * Language used to construct environment variable names that match PropertyDescriptors. - */ - private final String languageId; - - private final ParserOptionsProperties parserOptionsProperties; - - public ParserOptions() { - this.languageId = null; - this.parserOptionsProperties = new ParserOptionsProperties(); - } - - public ParserOptions(String languageId) { - this.languageId = Objects.requireNonNull(languageId); - this.parserOptionsProperties = new 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 - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null || getClass() != obj.getClass()) { - return false; - } - final ParserOptions that = (ParserOptions) obj; - return Objects.equals(suppressMarker, that.suppressMarker) - && Objects.equals(languageId, that.languageId) - && Objects.equals(parserOptionsProperties, that.parserOptionsProperties); - } - - @Override - public int hashCode() { - return Objects.hash(suppressMarker, languageId, parserOptionsProperties); - } - - - private final class ParserOptionsProperties extends AbstractPropertySource { - - @Override - protected String getPropertySourceType() { - return "ParserOptions"; - } - - @Override - 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(); - } - } -} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java index 9aa9fbdb77..4827508fbe 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/Parser.java @@ -53,12 +53,7 @@ public interface Parser { private final PropertySource propertySource; - public ParserTask(LanguageVersion lv, String filepath, String sourceText, SemanticErrorReporter reporter) { - this(lv, filepath, sourceText, reporter, PMD.SUPPRESS_MARKER); - } - - public ParserTask(LanguageVersion lv, String filepath, String sourceText, SemanticErrorReporter reporter, String commentMarker) { this.lv = Objects.requireNonNull(lv, "lv was null"); this.filepath = Objects.requireNonNull(filepath, "filepath was null"); this.sourceText = Objects.requireNonNull(sourceText, "sourceText was null"); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java index f34e442045..be57a2cbc7 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractDelegateRule.java @@ -12,7 +12,6 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.properties.MultiValuePropertyDescriptor; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -182,11 +181,6 @@ public abstract class AbstractDelegateRule implements Rule { rule.setPriority(priority); } - @Override - public ParserOptions getParserOptions() { - return rule.getParserOptions(); - } - @Override public void definePropertyDescriptor(PropertyDescriptor propertyDescriptor) throws IllegalArgumentException { rule.definePropertyDescriptor(propertyDescriptor); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java index cf1be67274..158c4f8cf3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/AbstractRule.java @@ -17,7 +17,6 @@ import net.sourceforge.pmd.RuleContext; import net.sourceforge.pmd.RulePriority; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.properties.AbstractPropertySource; @@ -227,18 +226,6 @@ public abstract class AbstractRule extends AbstractPropertySource implements Rul this.priority = priority; } - /** - * This implementation returns a new instance of {@link ParserOptions} using - * default settings. - * - * @see Rule#setPriority(RulePriority) - */ - @Override - @Deprecated - public ParserOptions getParserOptions() { - return new ParserOptions(); - } - private Set> getClassRuleChainVisits() { if (classRuleChainVisits.isEmpty() && ruleChainVisits.isEmpty()) { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java index 933f52771a..9f063a8d01 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java @@ -49,7 +49,7 @@ public class DummyLanguageModule extends BaseLanguageModule { @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return task -> { DummyRoot node = new DummyRoot(); node.setCoords(1, 1, 2, 10); @@ -64,7 +64,7 @@ public class DummyLanguageModule extends BaseLanguageModule { public static class HandlerWithParserThatThrows extends Handler { @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return task -> { throw new AssertionError("test error while parsing"); }; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java index 4fbb3b45f7..d9c90d568a 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageHandler.java @@ -8,7 +8,6 @@ import java.util.Arrays; import java.util.List; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.JavaParser; @@ -63,7 +62,7 @@ public class JavaLanguageHandler extends AbstractPmdLanguageVersionHandler { } @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return new JavaParser(levelChecker); } diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptHandler.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptHandler.java index e9b6091c03..a5381e0de5 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptHandler.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/EcmascriptHandler.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.ecmascript; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.ecmascript.ast.EcmascriptParser; @@ -18,7 +17,7 @@ class EcmascriptHandler extends AbstractPmdLanguageVersionHandler { } @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return new EcmascriptParser(rhinoVersion); } diff --git a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspHandler.java b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspHandler.java index 9b54e37ab0..5516d45e0b 100644 --- a/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspHandler.java +++ b/pmd-jsp/src/main/java/net/sourceforge/pmd/lang/jsp/JspHandler.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.jsp; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.jsp.ast.JspParser; @@ -17,7 +16,7 @@ import net.sourceforge.pmd.lang.jsp.ast.JspParser; public class JspHandler extends AbstractPmdLanguageVersionHandler { @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return new JspParser(); } diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index bae3c45066..e4f63e1de5 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -13,6 +13,8 @@ import java.io.File import java.io.InputStream import java.io.StringReader import java.nio.charset.StandardCharsets +import java.nio.file.Files +import java.nio.file.Path import java.util.function.Consumer /** @@ -30,7 +32,6 @@ abstract class BaseParsingHelper, T : RootNode val defaultVerString: String?, val resourceLoader: Class<*>?, val resourcePrefix: String, - val parserOptions: ParserOptions? = null, val configureParser: (PropertySource) -> Unit = {}, ) { companion object { @@ -94,13 +95,9 @@ abstract class BaseParsingHelper, T : RootNode /** - * Returns an instance of [Self] for which the [parse] methods use - * the provided [parserOptions]. + * Returns an instance of [Self] which configures the parser task with the + * given closure. */ - fun withParserOptions(parserOptions: ParserOptions?): Self = - clone(params.copy(parserOptions = parserOptions)) - - fun withParserConfig(configFun: Consumer): Self = clone(params.copy(configureParser = { configFun.accept(it) })) @@ -126,8 +123,7 @@ abstract class BaseParsingHelper, T : RootNode fun parse(sourceCode: String, version: String? = null): T { val lversion = if (version == null) defaultVersion else getVersion(version) val handler = lversion.languageVersionHandler - val options = params.parserOptions ?: handler.defaultParserOptions - val parser = handler.getParser(options) + val parser = handler.parser val source = DataSource.forString(sourceCode, FileAnalysisException.NO_FILE_NAME) val toString = DataSource.readToString(source, StandardCharsets.UTF_8) val task = Parser.ParserTask(lversion, FileAnalysisException.NO_FILE_NAME, toString, SemanticErrorReporter.noop()) @@ -175,6 +171,13 @@ abstract class BaseParsingHelper, T : RootNode open fun parseResource(resource: String, version: String? = null): T = parse(readResource(resource), version) + /** + * Fetches and [parse]s the [path]. + */ + @JvmOverloads + open fun parseFile(path: Path, version: String? = null): T = + parse(IOUtils.toString(Files.newBufferedReader(path)), version) + /** * Fetches the source of the given [clazz]. */ diff --git a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaHandler.java b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaHandler.java index 9230e08a31..1fe768cdb8 100644 --- a/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaHandler.java +++ b/pmd-modelica/src/main/java/net/sourceforge/pmd/lang/modelica/ModelicaHandler.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.modelica; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.modelica.ast.ModelicaParser; import net.sourceforge.pmd.lang.modelica.internal.ModelicaProcessingStage; @@ -19,7 +18,7 @@ public class ModelicaHandler extends AbstractPmdLanguageVersionHandler { @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return new ModelicaParser(); } diff --git a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLHandler.java b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLHandler.java index 63f31af944..a403100e40 100644 --- a/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLHandler.java +++ b/pmd-plsql/src/main/java/net/sourceforge/pmd/lang/plsql/PLSQLHandler.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.plsql; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.plsql.ast.PLSQLParser; @@ -23,7 +22,7 @@ public class PLSQLHandler extends AbstractPmdLanguageVersionHandler { } @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return new PLSQLParser(); } diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageHandler.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageHandler.java index f13a3c52b9..7f3d055f3b 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageHandler.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ScalaLanguageHandler.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.scala; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.scala.ast.ScalaParser; import scala.meta.Dialect; @@ -38,7 +37,7 @@ public class ScalaLanguageHandler extends AbstractPmdLanguageVersionHandler { @Override - public ScalaParser getParser(ParserOptions parserOptions) { + public ScalaParser getParser() { return new ScalaParser(dialect); } } diff --git a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftHandler.java b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftHandler.java index b5aec2c447..5f09696809 100644 --- a/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftHandler.java +++ b/pmd-swift/src/main/java/net/sourceforge/pmd/lang/swift/SwiftHandler.java @@ -5,14 +5,13 @@ package net.sourceforge.pmd.lang.swift; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.swift.ast.PmdSwiftParser; public class SwiftHandler extends AbstractPmdLanguageVersionHandler { @Override - public Parser getParser(final ParserOptions parserOptions) { + public Parser getParser() { return new PmdSwiftParser(); } } 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 deleted file mode 100644 index dc85fc706e..0000000000 --- a/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTest.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang; - -/** - * Unit tests for {@link ParserOptions}. - * @deprecated for removal in PMD 7.0. Use {@link ParserOptionsTestUtils}. - */ -@Deprecated -public class ParserOptionsTest { - - /** - * 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/main/java/net/sourceforge/pmd/lang/ParserOptionsTestUtils.java b/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTestUtils.java deleted file mode 100644 index d8612c4051..0000000000 --- a/pmd-test/src/main/java/net/sourceforge/pmd/lang/ParserOptionsTestUtils.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * BSD-style license; for more info see http://pmd.sourceforge.net/license.html - */ - -package net.sourceforge.pmd.lang; - -import org.junit.Assert; - -public final class ParserOptionsTestUtils { - private ParserOptionsTestUtils() { - } - - /** - * 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) { - // Objects should be different - Assert.assertNotSame(options1, options2); - Assert.assertNotSame(options1, options2); - Assert.assertNotSame(options1, options3); - Assert.assertNotSame(options2, options3); - Assert.assertNotSame(options2, options4); - Assert.assertNotSame(options3, options4); - - // Check all 16 equality combinations - Assert.assertEquals(options1, options1); - Assert.assertNotEquals(options1, options2); - Assert.assertEquals(options1, options3); - Assert.assertNotEquals(options1, options4); - - Assert.assertNotEquals(options2, options1); - Assert.assertEquals(options2, options2); - Assert.assertNotEquals(options2, options3); - Assert.assertEquals(options2, options4); - - Assert.assertEquals(options3, options1); - Assert.assertNotEquals(options3, options2); - Assert.assertEquals(options3, options3); - Assert.assertNotEquals(options3, options4); - - Assert.assertNotEquals(options4, options1); - Assert.assertEquals(options4, options2); - Assert.assertNotEquals(options4, options3); - Assert.assertEquals(options4, options4); - - // Hashcodes should match up - Assert.assertNotEquals(options1.hashCode(), options2.hashCode()); - Assert.assertEquals(options1.hashCode(), options3.hashCode()); - Assert.assertNotEquals(options1.hashCode(), options4.hashCode()); - Assert.assertNotEquals(options2.hashCode(), options3.hashCode()); - Assert.assertEquals(options2.hashCode(), options4.hashCode()); - Assert.assertNotEquals(options3.hashCode(), options4.hashCode()); - } -} diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java b/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java index f2bdb7b34d..6b26d9fe3c 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java @@ -10,7 +10,6 @@ import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; import net.sourceforge.pmd.lang.BaseLanguageModule; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.AstInfo; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.ast.RootNode; @@ -45,7 +44,7 @@ public class DummyLanguageModule extends BaseLanguageModule { } @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return task -> { DummyRootNode node = new DummyRootNode(); node.setCoords(1, 1, 1, 2); 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 97248ffeaa..6a3858f6b6 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 @@ -9,7 +9,6 @@ import java.util.Collections; import java.util.List; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.vf.ast.VfParser; import net.sourceforge.pmd.properties.PropertyDescriptor; @@ -46,7 +45,7 @@ public class VfHandler extends AbstractPmdLanguageVersionHandler { .build(); @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return new VfParser(); } diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java index 7b1eed7462..760ddb31d3 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java @@ -12,16 +12,17 @@ import java.nio.file.Path; import java.util.List; import java.util.logging.Logger; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.exception.ContextedRuntimeException; 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 net.sourceforge.pmd.lang.ast.Parser; +import net.sourceforge.pmd.lang.ast.Parser.ParserTask; +import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; import net.sourceforge.pmd.lang.vf.DataType; import apex.jorje.semantic.symbol.type.BasicType; @@ -47,18 +48,12 @@ class ApexClassPropertyTypes extends SalesforceFieldTypes { 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); + Node node = parse(expression, apexFilePath); + ApexClassPropertyTypesVisitor visitor = new ApexClassPropertyTypesVisitor(); + node.acceptVisitor(visitor, null); + + for (Pair variable : visitor.getVariables()) { + putDataType(variable.getKey(), DataType.fromBasicType(variable.getValue())); } if (containsExpression(expression)) { @@ -70,6 +65,29 @@ class ApexClassPropertyTypes extends SalesforceFieldTypes { } } + private Node parse(String expression, Path apexFilePath) { + String fileText; + try (BufferedReader reader = Files.newBufferedReader(apexFilePath, StandardCharsets.UTF_8)) { + fileText = IOUtils.toString(reader); + } catch (IOException e) { + throw new ContextedRuntimeException(e) + .addContextValue("expression", expression) + .addContextValue("apexFilePath", apexFilePath); + } + + LanguageVersion languageVersion = LanguageRegistry.getLanguage(ApexLanguageModule.NAME).getDefaultVersion(); + Parser parser = languageVersion.getLanguageVersionHandler().getParser(); + + ParserTask task = new ParserTask( + languageVersion, + apexFilePath.toString(), + fileText, + SemanticErrorReporter.noop() + ); + + return parser.parse(task); + } + @Override protected DataType putDataType(String name, DataType dataType) { DataType previousType = super.putDataType(name, dataType); @@ -91,9 +109,4 @@ class ApexClassPropertyTypes extends SalesforceFieldTypes { return 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/ast/ApexClassPropertyTypesVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitor.java index 5190aac350..13eeba6c62 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitor.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitor.java @@ -13,8 +13,7 @@ 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 net.sourceforge.pmd.lang.apex.ast.ApexVisitorBase; import apex.jorje.semantic.symbol.member.method.Generated; import apex.jorje.semantic.symbol.member.method.MethodInfo; @@ -23,7 +22,7 @@ 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 { +final class ApexClassPropertyTypesVisitor extends ApexVisitorBase { /** * Prefix for standard bean type getters, i.e. getFoo @@ -54,7 +53,7 @@ final class ApexClassPropertyTypesVisitor extends ApexParserVisitorAdapter { * Visualforce page. */ @Override - public Object visit(ASTMethod node, Object data) { + public Void visit(ASTMethod node, Void data) { MethodInfo mi = node.getNode().getMethodInfo(); if (mi.getParameterTypes().isEmpty() && isVisibleToVisualForce(node) @@ -76,7 +75,7 @@ final class ApexClassPropertyTypesVisitor extends ApexParserVisitorAdapter { variables.add(Pair.of(sb.toString(), mi.getReturnType().getBasicType())); } - return super.visit((ApexNode) node, data); + return visitApexNode(node, data); } /** diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitorTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitorTest.java index 4ec20b3dc8..280c7e1956 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitorTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypesVisitorTest.java @@ -5,28 +5,25 @@ package net.sourceforge.pmd.lang.vf.ast; 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.util.Hashtable; import java.util.List; import java.util.Map; +import org.apache.commons.io.IOUtils; 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 net.sourceforge.pmd.lang.ast.Parser; +import net.sourceforge.pmd.lang.ast.Parser.ParserTask; +import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; import net.sourceforge.pmd.lang.vf.VFTestUtils; import apex.jorje.semantic.symbol.type.BasicType; @@ -35,17 +32,16 @@ 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); + Parser parser = languageVersion.getLanguageVersionHandler().getParser(); Path apexPath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Apex) .resolve("ApexController.cls").toAbsolutePath(); + ParserTask task = new ParserTask(languageVersion, + apexPath.toString(), + IOUtils.toString(Files.newBufferedReader(apexPath)), + SemanticErrorReporter.noop()); 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); - } + parser.parse(task).acceptVisitor(visitor, null); List> variables = visitor.getVariables(); assertEquals(7, variables.size()); diff --git a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitorTest.java b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitorTest.java index 9a24cfeaf9..7a010cbd62 100644 --- a/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitorTest.java +++ b/pmd-visualforce/src/test/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitorTest.java @@ -9,8 +9,6 @@ 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; @@ -20,15 +18,10 @@ import java.util.Map.Entry; 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.ast.NodeStream; import net.sourceforge.pmd.lang.vf.DataType; import net.sourceforge.pmd.lang.vf.VFTestUtils; -import net.sourceforge.pmd.lang.vf.VfLanguageModule; import net.sourceforge.pmd.util.treeexport.XmlTreeRenderer; public class VfExpressionTypeVisitorTest { @@ -60,7 +53,7 @@ public class VfExpressionTypeVisitorTest { * Strings that use dot notation(Account.CreatedDate) result in ASTIdentifier nodes */ @Test - public void testXpathQueryForCustomFieldIdentifiers() throws FileNotFoundException { + public void testXpathQueryForCustomFieldIdentifiers() { Node rootNode = compile("StandardAccount.page"); for (Map.Entry entry : EXPECTED_CUSTOM_FIELD_DATA_TYPES.entrySet()) { @@ -82,7 +75,7 @@ public class VfExpressionTypeVisitorTest { * creates ambiguous situations with Apex methods that return Maps. This may be addressed in a future release. */ @Test - public void testXpathQueryForCustomFieldLiteralsHaveNullDataType() throws FileNotFoundException { + public void testXpathQueryForCustomFieldLiteralsHaveNullDataType() { Node rootNode = compile("StandardAccount.page"); for (Map.Entry entry : EXPECTED_CUSTOM_FIELD_DATA_TYPES.entrySet()) { @@ -107,7 +100,7 @@ public class VfExpressionTypeVisitorTest { * Nodes where the DataType can't be determined should have a null DataType */ @Test - public void testDataTypeForCustomFieldsNotFound() throws FileNotFoundException { + public void testDataTypeForCustomFieldsNotFound() { Node rootNode = compile("StandardAccount.page"); checkNodes(rootNode.descendants(ASTIdentifier.class) @@ -129,7 +122,7 @@ public class VfExpressionTypeVisitorTest { * Apex properties result in ASTIdentifier nodes */ @Test - public void testXpathQueryForProperties() throws FileNotFoundException { + public void testXpathQueryForProperties() { Node rootNode = compile("ApexController.page"); for (Map.Entry entry : EXPECTED_APEX_DATA_TYPES.entrySet()) { @@ -157,7 +150,7 @@ public class VfExpressionTypeVisitorTest { * Nodes where the DataType can't be determined should have a null DataType */ @Test - public void testDataTypeForApexPropertiesNotFound() throws FileNotFoundException { + public void testDataTypeForApexPropertiesNotFound() { Node rootNode = compile("ApexController.page"); // Each string appears twice, it is set on a "value" attribute and inline @@ -165,22 +158,18 @@ public class VfExpressionTypeVisitorTest { .filterMatching(ASTIdentifier::getImage, "NotFoundProp")); } - private Node compile(String pageName) throws FileNotFoundException { + private Node compile(String pageName) { return compile(pageName, false); } - private Node compile(String pageName, boolean renderAST) throws FileNotFoundException { + private Node compile(String pageName, boolean renderAST) { 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())); + private Node compile(Path vfPagePath, boolean renderAST) { + Node node = VfParsingHelper.DEFAULT.parseFile(vfPagePath); assertNotNull(node); if (renderAST) { 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 e58545d614..4ddf634b29 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,34 +4,37 @@ package net.sourceforge.pmd.lang.vf.rule.security; +import static net.sourceforge.pmd.util.CollectionUtil.listOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import java.util.List; +import org.apache.commons.io.IOUtils; import org.junit.Test; import net.sourceforge.pmd.PMD; +import net.sourceforge.pmd.PMDConfiguration; 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.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.ast.Parser; +import net.sourceforge.pmd.lang.ast.Parser.ParserTask; +import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; import net.sourceforge.pmd.lang.vf.VFTestUtils; import net.sourceforge.pmd.lang.vf.VfLanguageModule; import net.sourceforge.pmd.testframework.PmdRuleTst; +import net.sourceforge.pmd.util.datasource.FileDataSource; public class VfUnescapeElTest extends PmdRuleTst { public static final String EXPECTED_RULE_MESSAGE = "Avoid unescaped user controlled content in EL"; @@ -118,26 +121,28 @@ 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)} + * Runs a rule against a Visualforce page on the file system. */ - private Report runRule(Path vfPagePath) throws FileNotFoundException, PMDException { + private Report runRule(Path vfPagePath) throws IOException { LanguageVersion languageVersion = LanguageRegistry.getLanguage(VfLanguageModule.NAME).getDefaultVersion(); - ParserOptions parserOptions = languageVersion.getLanguageVersionHandler().getDefaultParserOptions(); - Parser parser = languageVersion.getLanguageVersionHandler().getParser(parserOptions); + ParserTask task = new ParserTask(languageVersion, + vfPagePath.toString(), + IOUtils.toString(Files.newBufferedReader(vfPagePath)), + SemanticErrorReporter.noop()); - Node node = parser.parse(vfPagePath.toString(), new FileReader(vfPagePath.toFile())); + Parser parser = languageVersion.getLanguageVersionHandler().getParser(); + + Node node = parser.parse(task); assertNotNull(node); - // BEGIN Based on RuleTst class - PMD p = new PMD(); - p.getConfiguration().setDefaultLanguageVersion(languageVersion); - p.getConfiguration().setIgnoreIncrementalAnalysis(true); + PMDConfiguration config = new PMDConfiguration(); + config.setDefaultLanguageVersion(languageVersion); + config.setIgnoreIncrementalAnalysis(true); // simple class loader, that doesn't delegate to parent. // this allows us in the tests to simulate PMD run without // auxclasspath, not even the classes from the test dependencies // will be found. - p.getConfiguration().setClassLoader(new ClassLoader() { + config.setClassLoader(new ClassLoader() { @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { if (name.startsWith("java.") || name.startsWith("javax.")) { @@ -146,18 +151,13 @@ public class VfUnescapeElTest extends PmdRuleTst { throw new ClassNotFoundException(name); } }); - Rule rule = findRule("category/vf/security.xml", "VfUnescapeEl"); - Report report = new Report(); - RuleContext ctx = new RuleContext(); - ctx.setReport(report); - ctx.setSourceCodeFile(vfPagePath.toFile()); - ctx.setLanguageVersion(languageVersion); - ctx.setIgnoreExceptions(false); - RuleSet rules = RuleSet.forSingleRule(rule); - p.getSourceCodeProcessor().processSourceCode(new FileReader(vfPagePath.toFile()), new RuleSets(rules), ctx); - // END Based on RuleTst class - return report; + return PMD.processFiles( + config, + listOf(RuleSet.forSingleRule(rule)), + listOf(new FileDataSource(vfPagePath.toFile())), + Collections.emptyList() + ); } } diff --git a/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/VmHandler.java b/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/VmHandler.java index b2bafd61c0..cac346da69 100644 --- a/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/VmHandler.java +++ b/pmd-vm/src/main/java/net/sourceforge/pmd/lang/vm/VmHandler.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.vm; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.vm.ast.VmParser; @@ -17,7 +16,7 @@ public class VmHandler extends AbstractPmdLanguageVersionHandler { @Override - public Parser getParser(final ParserOptions parserOptions) { + public Parser getParser() { return new VmParser(); } diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlHandler.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlHandler.java index aa6404ec54..3e0a138800 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlHandler.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/XmlHandler.java @@ -5,7 +5,6 @@ package net.sourceforge.pmd.lang.xml; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; -import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.ast.Parser; /** @@ -14,7 +13,7 @@ import net.sourceforge.pmd.lang.ast.Parser; public class XmlHandler extends AbstractPmdLanguageVersionHandler { @Override - public Parser getParser(ParserOptions parserOptions) { + public Parser getParser() { return new XmlParser(); } From 08227f8b18badf9990d3bd55d3544215ee77beab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 13 Dec 2020 00:56:03 +0100 Subject: [PATCH 296/299] Fix vf module --- .../java/net/sourceforge/pmd/RuleContext.java | 2 +- .../sourceforge/pmd/SourceCodeProcessor.java | 32 ++--------------- .../pmd/processor/PmdRunnable.java | 2 ++ .../net/sourceforge/pmd/RuleContextTest.java | 7 ++-- .../sourceforge/pmd/lang/java/types/Lub.java | 6 ---- .../pmd/lang/ast/test/BaseParsingHelper.kt | 8 ++--- .../pmd/lang/vf/ast/SalesforceFieldTypes.java | 1 + .../vf/rule/security/VfUnescapeElTest.java | 34 +++++-------------- .../pmd/lang/xml/ast/testdata/sampleNs.xml | 4 --- 9 files changed, 22 insertions(+), 74 deletions(-) diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java b/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java index 5690591011..9d9672ac0c 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/RuleContext.java @@ -100,7 +100,7 @@ public class RuleContext { */ public String getSourceCodeFilename() { if (sourceCodeFile != null) { - return sourceCodeFile.getName(); + return sourceCodeFile.getAbsolutePath(); } return ""; } 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 fec1de009d..64b9cb8fb4 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java @@ -138,36 +138,6 @@ public class SourceCodeProcessor { private void processSource(Reader reader, RuleSets ruleSets, RuleContext ctx) throws IOException { - - // basically: - // 1. make the union of all stage dependencies of each rule, by language, for the Rulesets - // 2. order them by dependency - // 3. run them and time them if needed - - // The problem is the first two steps need only be done once. - // They're probably costly and if we do this here without changing anything, - // they'll be done on each file! Btw currently the "usesDfa" and such are nested loops testing - // all rules of all rulesets, but they're run on each file too! - - // FIXME - this implementation is a hack to wire-in stages without - // needing to change RuleSet immediately - - // With mutable RuleSets, caching of the value can't be guaranteed to be accurate... - // The approach I'd like to take is either - // * to create a new RunnableRulesets class which is immutable, and performs all these preliminary - // computations upon construction. - // * or to modify Ruleset and Rulesets to be immutable. This IMO is a better option because it makes - // these objects easier to reason about and pass around from thread to thread. It also avoid creating - // a new class, and breaking SourceCodeProcessor's API too much. - // - // The "preliminary computations" also include: - // * removing dysfunctional rules - // * separating rulechain rules from normal rules - // * grouping rules by language/ file extension - // * etc. - - - LanguageVersion languageVersion = ctx.getLanguageVersion(); String filename = ctx.getSourceCodeFilename(); String sourceCode = IOUtils.toString(reader); @@ -179,6 +149,8 @@ public class SourceCodeProcessor { SemanticErrorReporter.noop() // TODO ); + languageVersion.getLanguageVersionHandler().declareParserTaskProperties(task.getProperties()); + task.getProperties().setProperty(ParserTask.COMMENT_MARKER, configuration.getSuppressMarker()); Parser parser = languageVersion.getLanguageVersionHandler().getParser(); 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 da91fbd95a..5ef524ebfd 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 @@ -5,6 +5,7 @@ package net.sourceforge.pmd.processor; import java.io.BufferedInputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.List; @@ -82,6 +83,7 @@ public class PmdRunnable implements Callable { try (InputStream stream = new BufferedInputStream(dataSource.getInputStream())) { tc.ruleContext.setLanguageVersion(null); + tc.ruleContext.setSourceCodeFile(new File(dataSource.getNiceFileName(false, null))); sourceCodeProcessor.processSourceCode(stream, tc.ruleSets, tc.ruleContext); } catch (PMDException pmde) { addError(report, pmde, "Error while processing file: " + fileName); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleContextTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleContextTest.java index ba5534e574..69d3ff192d 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleContextTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleContextTest.java @@ -22,7 +22,7 @@ import net.sourceforge.pmd.lang.ast.RootNode; import junit.framework.JUnit4TestAdapter; public class RuleContextTest { - public static Report getReport(Consumer sideEffects) throws Exception { + public static Report getReport(Consumer sideEffects) { Report report = new Report(); RuleContext ctx = new RuleContext(); ctx.setSourceCodeFile(new File("test.dummy")); @@ -58,8 +58,9 @@ public class RuleContextTest { public void testSourceCodeFilename() { RuleContext ctx = new RuleContext(); assertEquals("filename should be empty", "", ctx.getSourceCodeFilename()); - ctx.setSourceCodeFile(new File("dir/foo.java")); - assertEquals("filename mismatch", "foo.java", ctx.getSourceCodeFilename()); + File file = new File("dir/foo.java"); + ctx.setSourceCodeFile(file); + assertEquals("filename mismatch", file.getAbsolutePath(), ctx.getSourceCodeFilename()); } @Test diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java index e39c71baf6..154db01020 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/Lub.java @@ -356,12 +356,6 @@ final class Lub { } } - private static void checkGlbComponent(Collection types, JTypeMirror ci) { - if (ci.isPrimitive() || ci instanceof JWildcardType || ci instanceof JIntersectionType) { - throw new IllegalArgumentException("Bad intersection type component: " + ci + " in " + types); - } - } - private static @NonNull List flattenRemoveTrivialBound(Collection types) { List bounds = new ArrayList<>(types.size()); diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index e4f63e1de5..97c30f713b 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -120,13 +120,13 @@ abstract class BaseParsingHelper, T : RootNode * so. */ @JvmOverloads - fun parse(sourceCode: String, version: String? = null): T { + fun parse(sourceCode: String, version: String? = null, filename: String = FileAnalysisException.NO_FILE_NAME): T { val lversion = if (version == null) defaultVersion else getVersion(version) val handler = lversion.languageVersionHandler val parser = handler.parser - val source = DataSource.forString(sourceCode, FileAnalysisException.NO_FILE_NAME) + val source = DataSource.forString(sourceCode, filename) val toString = DataSource.readToString(source, StandardCharsets.UTF_8) - val task = Parser.ParserTask(lversion, FileAnalysisException.NO_FILE_NAME, toString, SemanticErrorReporter.noop()) + val task = Parser.ParserTask(lversion, filename, toString, SemanticErrorReporter.noop()) task.properties.also { handler.declareParserTaskProperties(it) params.configureParser(it) @@ -176,7 +176,7 @@ abstract class BaseParsingHelper, T : RootNode */ @JvmOverloads open fun parseFile(path: Path, version: String? = null): T = - parse(IOUtils.toString(Files.newBufferedReader(path)), version) + parse(IOUtils.toString(Files.newBufferedReader(path)), version, filename = path.toAbsolutePath().toString()) /** * Fetches the source of the given [clazz]. diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/SalesforceFieldTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/SalesforceFieldTypes.java index 3cbf975533..e1514cd42f 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/SalesforceFieldTypes.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/SalesforceFieldTypes.java @@ -55,6 +55,7 @@ abstract class SalesforceFieldTypes { // The expression has been previously requested, but was not found return null; } else { + // fixme getting a Path from the display name is just wrong. Path vfFilePath = Paths.get(vfFileName); List resolvedPaths = new ArrayList<>(); for (String metadataDirectory : metadataDirectories) { 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 4ddf634b29..d9fc3f2254 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 @@ -8,31 +8,22 @@ import static net.sourceforge.pmd.util.CollectionUtil.listOf; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collections; import java.util.List; -import org.apache.commons.io.IOUtils; import org.junit.Test; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.PMDConfiguration; -import net.sourceforge.pmd.PMDException; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleViolation; -import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.ast.Parser; -import net.sourceforge.pmd.lang.ast.Parser.ParserTask; -import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; import net.sourceforge.pmd.lang.vf.VFTestUtils; -import net.sourceforge.pmd.lang.vf.VfLanguageModule; +import net.sourceforge.pmd.lang.vf.ast.VfParsingHelper; import net.sourceforge.pmd.testframework.PmdRuleTst; import net.sourceforge.pmd.util.datasource.FileDataSource; @@ -43,7 +34,7 @@ public class VfUnescapeElTest extends PmdRuleTst { * Verify that CustomFields stored in sfdx project format are correctly parsed */ @Test - public void testSfdxCustomFields() throws IOException, PMDException { + public void testSfdxCustomFields() { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) .resolve("StandardAccount.page"); @@ -68,7 +59,7 @@ public class VfUnescapeElTest extends PmdRuleTst { * Verify that CustomFields stored in mdapi format are correctly parsed */ @Test - public void testMdapiCustomFields() throws IOException, PMDException { + public void testMdapiCustomFields() { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.MDAPI, VFTestUtils.MetadataType.Vf).resolve("StandardAccount.page"); Report report = runRule(vfPagePath); @@ -86,7 +77,7 @@ public class VfUnescapeElTest extends PmdRuleTst { * Tests a page with a single Apex controller */ @Test - public void testApexController() throws IOException, PMDException { + public void testApexController() { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf).resolve("ApexController.page"); Report report = runRule(vfPagePath); @@ -105,7 +96,7 @@ public class VfUnescapeElTest extends PmdRuleTst { * Tests a page with a standard controller and two Apex extensions */ @Test - public void testExtensions() throws IOException, PMDException { + public void testExtensions() { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) .resolve(Paths.get("StandardAccountWithExtensions.page")); @@ -123,20 +114,11 @@ public class VfUnescapeElTest extends PmdRuleTst { /** * Runs a rule against a Visualforce page on the file system. */ - private Report runRule(Path vfPagePath) throws IOException { - LanguageVersion languageVersion = LanguageRegistry.getLanguage(VfLanguageModule.NAME).getDefaultVersion(); - ParserTask task = new ParserTask(languageVersion, - vfPagePath.toString(), - IOUtils.toString(Files.newBufferedReader(vfPagePath)), - SemanticErrorReporter.noop()); - - Parser parser = languageVersion.getLanguageVersionHandler().getParser(); - - Node node = parser.parse(task); + private Report runRule(Path vfPagePath) { + Node node = VfParsingHelper.DEFAULT.parseFile(vfPagePath); assertNotNull(node); PMDConfiguration config = new PMDConfiguration(); - config.setDefaultLanguageVersion(languageVersion); config.setIgnoreIncrementalAnalysis(true); // simple class loader, that doesn't delegate to parent. // this allows us in the tests to simulate PMD run without @@ -156,7 +138,7 @@ public class VfUnescapeElTest extends PmdRuleTst { return PMD.processFiles( config, listOf(RuleSet.forSingleRule(rule)), - listOf(new FileDataSource(vfPagePath.toFile())), + listOf(new FileDataSource(vfPagePath.toAbsolutePath().toFile())), Collections.emptyList() ); } diff --git a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.xml b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.xml index 776a795cd1..3d242f0de8 100644 --- a/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.xml +++ b/pmd-xml/src/test/resources/net/sourceforge/pmd/lang/xml/ast/testdata/sampleNs.xml @@ -1,8 +1,4 @@ - - Date: Sun, 13 Dec 2020 01:31:27 +0100 Subject: [PATCH 297/299] Fix some test files not being compilable --- .../bestpractices/xml/UnusedAssignment.xml | 24 +++++++++++-------- .../codestyle/xml/FieldNamingConventions.xml | 2 ++ .../rule/codestyle/xml/GenericsNaming.xml | 8 +++---- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml index 62459c0442..bf11084399 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedAssignment.xml @@ -1722,6 +1722,10 @@ class Foo { FP with anonymous classes on the way 0 false-positive in DD-part of DataflowAnomalyAnalysis #1675 0 formatterClass = findClass(formatterClassName); formatter = formatterClass.getDeclaredConstructor().newInstance(); - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException ignored) { + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException ignored) { } } - setFormatter((formatter == null) ? new SimpleFormatter() : formatter); + return ((formatter == null) ? new Formatter() : formatter); } } ]]> @@ -3345,12 +3349,12 @@ public class UnusedAssignmentNative { 0 interceptors; + class Test { + private final List interceptors; private int preProcessIndex = -1; - public void applyPreProcess(NativeWebRequest request, Callable task) throws Exception { - for (CallableProcessingInterceptor interceptor : this.interceptors) { + public void applyPreProcess(String request, java.util.concurrent.Callable task) throws Exception { + for (T interceptor : this.interceptors) { interceptor.preProcess(request, task); this.preProcessIndex++; } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml index f2e821e3fd..1ee74d2ba5 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/codestyle/xml/FieldNamingConventions.xml @@ -193,6 +193,8 @@ public class MyException extends RuntimeException { Exclude serialPersistentFields by default 0 1 upper case/single letter 0 +public interface Foo { } ]]> @@ -18,7 +18,7 @@ public interface Foo 2 upper case/single letter 0 +public interface Foo > { } ]]> @@ -28,7 +28,7 @@ public interface Foo 1 lower Case/single letter 1 +public interface Foo { } ]]> @@ -38,7 +38,7 @@ public interface Foo 1 lower case/multiple letter 1 +public interface Foo > { } ]]> From 52d8904a9e56f0acfbb07d8fe12eef3e260936bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 13 Dec 2020 01:41:07 +0100 Subject: [PATCH 298/299] Fix last tests --- .../sourceforge/pmd/SourceCodeProcessor.java | 2 +- .../net/sourceforge/pmd/ExcludeLinesTest.java | 3 +-- .../ecmascript/ast/EcmascriptParserTest.java | 3 +-- .../pmd/lang/ast/test/BaseParsingHelper.kt | 25 ++++++++----------- 4 files changed, 14 insertions(+), 19 deletions(-) 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 64b9cb8fb4..040d3cb1df 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/SourceCodeProcessor.java @@ -149,8 +149,8 @@ public class SourceCodeProcessor { SemanticErrorReporter.noop() // TODO ); + // todo following 2 lines should be deleted languageVersion.getLanguageVersionHandler().declareParserTaskProperties(task.getProperties()); - task.getProperties().setProperty(ParserTask.COMMENT_MARKER, configuration.getSuppressMarker()); Parser parser = languageVersion.getLanguageVersionHandler().getParser(); 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 e04e9726fe..4de7893d34 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/ExcludeLinesTest.java @@ -9,7 +9,6 @@ import static net.sourceforge.pmd.lang.ast.test.TestUtilsKt.assertSuppressed; import org.junit.Test; -import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.java.ast.ASTVariableDeclaratorId; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRule; import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest; @@ -38,7 +37,7 @@ public class ExcludeLinesTest extends BaseNonParserTest { @Test public void testAlternateMarker() { - Report rpt = java.withParserConfig(p -> p.setProperty(ParserTask.COMMENT_MARKER, "FOOBAR")).executeRule(getRule(), TEST3); + Report rpt = java.withSuppressMarker("FOOBAR").executeRule(getRule(), TEST3); assertSize(rpt, 0); assertSuppressed(rpt, 1); } diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java index 3e9331913f..deb4bc0758 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java @@ -17,7 +17,6 @@ import org.mozilla.javascript.ast.AstRoot; import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.lang.ast.Node; -import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.ecmascript.rule.AbstractEcmascriptRule; public class EcmascriptParserTest extends EcmascriptParserTestBase { @@ -150,7 +149,7 @@ public class EcmascriptParserTest extends EcmascriptParserTestBase { assertEquals(" I know what I'm doing", root.getAstInfo().getSuppressionComments().get(2)); assertEquals(1, root.getAstInfo().getSuppressionComments().size()); - root = js.withParserConfig(p -> p.setProperty(ParserTask.COMMENT_MARKER, "FOOOO")) + root = js.withSuppressMarker("FOOOO") .parse("function(x) {\n" + "y = y; //NOPMD xyz\n" + "x = x; //FOOOO I know what I'm doing\n" + "}\n"); assertEquals(" I know what I'm doing", root.getAstInfo().getSuppressionComments().get(3)); assertEquals(1, root.getAstInfo().getSuppressionComments().size()); diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index 97c30f713b..cf7a3c8723 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -4,9 +4,11 @@ package net.sourceforge.pmd.lang.ast.test import net.sourceforge.pmd.* -import net.sourceforge.pmd.lang.* +import net.sourceforge.pmd.lang.Language +import net.sourceforge.pmd.lang.LanguageRegistry +import net.sourceforge.pmd.lang.LanguageVersion +import net.sourceforge.pmd.lang.LanguageVersionHandler import net.sourceforge.pmd.lang.ast.* -import net.sourceforge.pmd.properties.PropertySource import net.sourceforge.pmd.util.datasource.DataSource import org.apache.commons.io.IOUtils import java.io.File @@ -15,7 +17,6 @@ import java.io.StringReader import java.nio.charset.StandardCharsets import java.nio.file.Files import java.nio.file.Path -import java.util.function.Consumer /** * Language-independent base for a parser utils class. @@ -32,7 +33,7 @@ abstract class BaseParsingHelper, T : RootNode val defaultVerString: String?, val resourceLoader: Class<*>?, val resourcePrefix: String, - val configureParser: (PropertySource) -> Unit = {}, + val suppressMarker: String = PMD.SUPPRESS_MARKER, ) { companion object { @@ -94,13 +95,8 @@ abstract class BaseParsingHelper, T : RootNode clone(params.copy(resourceLoader = contextClass, resourcePrefix = resourcePrefix)) - /** - * Returns an instance of [Self] which configures the parser task with the - * given closure. - */ - fun withParserConfig(configFun: Consumer): Self = - clone(params.copy(configureParser = { configFun.accept(it) })) - + fun withSuppressMarker(marker: String): Self = + clone(params.copy(suppressMarker = marker)) fun getHandler(version: String): LanguageVersionHandler { return getVersion(version).languageVersionHandler @@ -125,11 +121,11 @@ abstract class BaseParsingHelper, T : RootNode val handler = lversion.languageVersionHandler val parser = handler.parser val source = DataSource.forString(sourceCode, filename) - val toString = DataSource.readToString(source, StandardCharsets.UTF_8) + val toString = DataSource.readToString(source, StandardCharsets.UTF_8) // this removed the BOM val task = Parser.ParserTask(lversion, filename, toString, SemanticErrorReporter.noop()) task.properties.also { handler.declareParserTaskProperties(it) - params.configureParser(it) + it.setProperty(Parser.ParserTask.COMMENT_MARKER, params.suppressMarker) } val rootNode = rootClass.cast(parser.parse(task)) if (params.doProcess) { @@ -226,12 +222,13 @@ abstract class BaseParsingHelper, T : RootNode @JvmOverloads fun executeRule(rule: Rule, code: String, filename: String = "testfile.${language.extensions[0]}"): Report { val p = PMD() - // p.configuration.suppressMarker = this.params.parserOptions?.suppressMarker ?: PMD.SUPPRESS_MARKER + p.configuration.suppressMarker = this.params.suppressMarker val ctx = RuleContext() val report = Report() ctx.report = report ctx.sourceCodeFile = File(filename) ctx.isIgnoreExceptions = false + val rules = RuleSet.forSingleRule(rule) try { p.sourceCodeProcessor.processSourceCode(StringReader(code), RuleSets(rules), ctx) From fe5d1e49f13f549339e285521d4bf19f35ac524d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sun, 13 Dec 2020 03:55:14 +0100 Subject: [PATCH 299/299] Fix tests --- .../main/java/net/sourceforge/pmd/PMD.java | 6 ++- .../pmd/docs/GenerateRuleDocsCmd.java | 50 ------------------- .../xpath/internal/BaseXPathFunctionTest.java | 11 ++-- .../pmd/lang/ast/test/BaseParsingHelper.kt | 2 +- .../pmd/lang/vf/ast/SalesforceFieldTypes.java | 2 +- .../vf/rule/security/VfUnescapeElTest.java | 10 ++-- 6 files changed, 20 insertions(+), 61 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 393888d0f5..2fc4f4e796 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 final class PMD { * as parameters * @param ruleSets Parsed rulesets * @param files Files to process, will be closed by this method. - * @param renderers Renderers that render the report + * @param renderers Renderers that render the report (may be empty) * * @return Report in which violations are accumulated * @@ -248,6 +248,10 @@ public final class PMD { } private static GlobalAnalysisListener createComposedRendererListener(List renderers) throws Exception { + if (renderers.isEmpty()) { + return GlobalAnalysisListener.noop(); + } + List rendererListeners = new ArrayList<>(renderers.size()); for (Renderer renderer : renderers) { try { 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 5bfe19eba3..aa1288abe0 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 @@ -6,7 +6,6 @@ package net.sourceforge.pmd.docs; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.nio.file.FileSystems; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -15,23 +14,15 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.List; -import java.util.Properties; -import java.util.logging.Logger; import java.util.regex.Pattern; import org.apache.commons.io.FilenameUtils; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetLoader; -import net.sourceforge.pmd.RuleSetLoader; -import net.sourceforge.pmd.RuleSetNotFoundException; -import net.sourceforge.pmd.lang.Language; -import net.sourceforge.pmd.lang.LanguageRegistry; public final class GenerateRuleDocsCmd { - private static final Logger LOG = Logger.getLogger(GenerateRuleDocsCmd.class.getName()); - private GenerateRuleDocsCmd() { // Utility class } @@ -57,47 +48,6 @@ public final class GenerateRuleDocsCmd { } - /** - * Returns an Iterator of RuleSet objects loaded from descriptions from the - * "categories.properties" resource for each Language with Rule support. - * - * @return An Iterator of RuleSet objects. - * - * @throws RuleSetNotFoundException if the ruleset file could not be found - */ - private static Iterable getRegisteredRuleSets() throws RuleSetNotFoundException { - String rulesetsProperties; - RuleSetLoader parser = new RuleSetLoader().warnDeprecated(false).includeDeprecatedRuleReferences(true); - - List ruleSets = new ArrayList<>(); - for (Language language : LanguageRegistry.getLanguages()) { - Properties props = new Properties(); - rulesetsProperties = "category/" + language.getTerseName() + "/categories.properties"; - - InputStream resource = GenerateRuleDocsCmd.class.getResourceAsStream(rulesetsProperties); - if (resource == null) { - LOG.warning("The language " + language.getTerseName() + " provides no " + rulesetsProperties + "."); - continue; - } - - try (InputStream inputStream = resource) { - props.load(inputStream); - String rulesetFilenames = props.getProperty("rulesets.filenames"); - - if (rulesetFilenames != null) { - - ruleSets.addAll(parser.loadFromResources(rulesetFilenames.split(","))); - - } - } 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 ruleSets; - } - public static List findAdditionalRulesets(Path basePath) { try { List additionalRulesets = new ArrayList<>(); diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java index 86ae4e3389..b7306f39c6 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/xpath/internal/BaseXPathFunctionTest.java @@ -5,6 +5,7 @@ package net.sourceforge.pmd.lang.java.rule.xpath.internal; import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; import java.util.function.Consumer; @@ -15,6 +16,7 @@ import org.junit.Assert; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.ast.FileAnalysisException; import net.sourceforge.pmd.lang.ast.test.TestUtilsKt; import net.sourceforge.pmd.lang.java.JavaLanguageModule; import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest; @@ -57,10 +59,13 @@ public class BaseXPathFunctionTest extends BaseNonParserTest { Rule rule = makeXpathRuleFromXPath(xpath); - PmdXPathException thrown = Assert.assertThrows(PmdXPathException.class, () -> executeRule(rule, code)); + FileAnalysisException thrown = Assert.assertThrows(FileAnalysisException.class, () -> executeRule(rule, code)); - exceptionSpec.accept(thrown); - assertThat(thrown.getRuleName(), equalTo(RULE_NAME_PLACEHOLDER)); + assertThat(thrown.getCause(), instanceOf(PmdXPathException.class)); + + PmdXPathException cause = (PmdXPathException) thrown.getCause(); + exceptionSpec.accept(cause); + assertThat(cause.getRuleName(), equalTo(RULE_NAME_PLACEHOLDER)); } diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index bf3b03afbe..58a58e3176 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -63,7 +63,7 @@ abstract class BaseParsingHelper, T : RootNode else language.getVersion(version) ?: throw AssertionError("Unsupported version $version for language $language") } - private val language: Language + val language: Language get() = LanguageRegistry.getLanguage(langName) ?: throw AssertionError("'$langName' is not a supported language (available ${LanguageRegistry.getLanguages()})") diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/SalesforceFieldTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/SalesforceFieldTypes.java index e1514cd42f..3faeb5e563 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/SalesforceFieldTypes.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/SalesforceFieldTypes.java @@ -56,7 +56,7 @@ abstract class SalesforceFieldTypes { return null; } else { // fixme getting a Path from the display name is just wrong. - Path vfFilePath = Paths.get(vfFileName); + Path vfFilePath = Paths.get(vfFileName).toAbsolutePath(); List resolvedPaths = new ArrayList<>(); for (String metadataDirectory : metadataDirectories) { if (Paths.get(metadataDirectory).isAbsolute()) { 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 d9fc3f2254..3cd0019f9b 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 @@ -34,7 +34,7 @@ public class VfUnescapeElTest extends PmdRuleTst { * Verify that CustomFields stored in sfdx project format are correctly parsed */ @Test - public void testSfdxCustomFields() { + public void testSfdxCustomFields() throws Exception { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) .resolve("StandardAccount.page"); @@ -59,7 +59,7 @@ public class VfUnescapeElTest extends PmdRuleTst { * Verify that CustomFields stored in mdapi format are correctly parsed */ @Test - public void testMdapiCustomFields() { + public void testMdapiCustomFields() throws Exception { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.MDAPI, VFTestUtils.MetadataType.Vf).resolve("StandardAccount.page"); Report report = runRule(vfPagePath); @@ -77,7 +77,7 @@ public class VfUnescapeElTest extends PmdRuleTst { * Tests a page with a single Apex controller */ @Test - public void testApexController() { + public void testApexController() throws Exception { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf).resolve("ApexController.page"); Report report = runRule(vfPagePath); @@ -96,7 +96,7 @@ public class VfUnescapeElTest extends PmdRuleTst { * Tests a page with a standard controller and two Apex extensions */ @Test - public void testExtensions() { + public void testExtensions() throws Exception { Path vfPagePath = VFTestUtils.getMetadataPath(this, VFTestUtils.MetadataFormat.SFDX, VFTestUtils.MetadataType.Vf) .resolve(Paths.get("StandardAccountWithExtensions.page")); @@ -114,7 +114,7 @@ public class VfUnescapeElTest extends PmdRuleTst { /** * Runs a rule against a Visualforce page on the file system. */ - private Report runRule(Path vfPagePath) { + private Report runRule(Path vfPagePath) throws Exception { Node node = VfParsingHelper.DEFAULT.parseFile(vfPagePath); assertNotNull(node);