diff --git a/docs/_config.yml b/docs/_config.yml index e6fee403f1..c308c5c33d 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,9 +1,9 @@ repository: pmd/pmd pmd: - version: 6.11.0 - previous_version: 6.10.0 - date: ??-January-2019 + version: 6.12.0 + previous_version: 6.11.0 + date: ??-February-2019 release_type: minor output: web diff --git a/docs/_plugins/javadoc_tag.rb b/docs/_plugins/javadoc_tag.rb index 7bdf60560c..940c2826e2 100644 --- a/docs/_plugins/javadoc_tag.rb +++ b/docs/_plugins/javadoc_tag.rb @@ -97,7 +97,7 @@ class JavadocTag < Liquid::Tag QNAME_NO_NAMESPACE_REGEX = /((?:\w+\.)*\w+)/ ARG_REGEX = Regexp.new(Regexp.union(JDocNamespaceDeclaration::NAMESPACED_FQCN_REGEX, QNAME_NO_NAMESPACE_REGEX).source + '(\[\])*') - ARGUMENTS_REGEX = Regexp.new('\(\)|\((' + ARG_REGEX.source + "(?:," + ARG_REGEX.source + ")*" + ')\)') + ARGUMENTS_REGEX = Regexp.new('\(\)|\((' + ARG_REGEX.source + "(?:,(?:" + ARG_REGEX.source + "))*" + ')\)') def initialize(tag_name, doc_ref, tokens) diff --git a/docs/pages/next_major_development.md b/docs/pages/next_major_development.md index 616dcae65c..456e9fb1cb 100644 --- a/docs/pages/next_major_development.md +++ b/docs/pages/next_major_development.md @@ -73,6 +73,16 @@ 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.11.0 + +* {% jdoc core::lang.rule.stat.StatisticalRule %} and the related helper classes and base rule classes +are deprecated for removal in 7.0.0. This includes all of {% jdoc_package core::stat %} and {% jdoc_package core::lang.rule.stat %}, +and also {% jdoc java::lang.java.rule.AbstractStatisticalJavaRule %}, {% jdoc apex::lang.apex.rule.AbstractStatisticalApexRule %} and the like. +The methods {% jdoc !c!core::Report#addMetric(core::stat.Metric) %} and {% jdoc core::ThreadSafeReportListener#metricAdded(core::stat.Metric) %} +will also be removed. +* {% jdoc core::properties.PropertySource#setProperty(core::properties.MultiValuePropertyDescriptor, Object[]) %} is deprecated, +because {% jdoc core::properties.MultiValuePropertyDescriptor %} is deprecated as well + #### 6.10.0 ##### Properties framework diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 43a401f025..b8f8783555 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -14,98 +14,11 @@ This is a {{ site.pmd.release_type }} release. ### New and noteworthy -### Updatex Apex Support - -* The Apex language support has been bumped to version 45 (Spring '19). All new language features are now properly - parsed and processed. - -#### New Rules - -* The new Java rule {% rule "java/multithreading/UnsynchronizedStaticFormatter" %} (`java-multithreading`) detects - unsynchronized usages of static `java.text.Format` instances. This rule is a more generic replacement of the - rule {% rule "java/multithreading/UnsynchronizedStaticDateFormatter" %} which focused just on `DateFormat`. - -* The new Java rule {% rule "java/bestpractices/ForLoopVariableCount" %} (`java-bestpractices`) checks for - the number of control variables in a for-loop. Having a lot of control variables makes it harder to understand - what the loop does. The maximum allowed number of variables is by default 1 and can be configured by a - property. - -* The new Java rule {% rule "java/bestpractices/AvoidReassigningLoopVariables" %} (`java-bestpractices`) searches - for loop variables that are reassigned. Changing the loop variables additionally to the loop itself can lead to - hard-to-find bugs. - -* The new Java rule {% rule "java/codestyle/UseDiamondOperator" %} (`java-codestyle`) looks for constructor - calls with explicit type parameters. Since Java 1.7, these type parameters are not necessary anymore, as they - can be inferred now. - -#### Modified Rules - -* The Java rule {% rule "java/codestyle/LocalVariableCouldBeFinal" %} (`java-codestyle`) has a new - property `ignoreForEachDecl`, which is by default disabled. The new property allows for ignoring - non-final loop variables in a for-each statement. - -#### Deprecated Rules - -* The Java rule {% rule "java/multithreading/UnsynchronizedStaticDateFormatter" %} has been deprecated and - will be removed with PMD 7.0.0. The rule is replaced by the more general - {% rule "java/multithreading/UnsynchronizedStaticFormatter" %}. - ### Fixed Issues -* all - * [#1196](https://github.com/pmd/pmd/issues/1196): \[core] CPD results not consistent between runs -* apex - * [#1542](https://github.com/pmd/pmd/pull/1542): \[apex] Include the documentation category - * [#1546](https://github.com/pmd/pmd/issues/1546): \[apex] PMD parsing exception for Apex classes using 'inherited sharing' keyword -* java - * [#1556](https://github.com/pmd/pmd/issues/1556): \[java] Default methods should not be considered abstract - * [#1578](https://github.com/pmd/pmd/issues/1578): \[java] Private field is detected as public inside nested classes in interfaces -* java-bestpractices - * [#658](https://github.com/pmd/pmd/issues/658): \[java] OneDeclarationPerLine: False positive for loops - * [#1518](https://github.com/pmd/pmd/issues/1518): \[java] New rule: AvoidReassigningLoopVariable - * [#1519](https://github.com/pmd/pmd/issues/1519): \[java] New rule: ForLoopVariableCount -* java-codestyle - * [#1513](https://github.com/pmd/pmd/issues/1513): \[java] LocalVariableCouldBeFinal: allow excluding the variable in a for-each loop - * [#1517](https://github.com/pmd/pmd/issues/1517): \[java] New Rule: UseDiamondOperator -* java-errorprone - * [#1035](https://github.com/pmd/pmd/issues/1035): \[java] ReturnFromFinallyBlock: False positive on lambda expression in finally block - * [#1549](https://github.com/pmd/pmd/issues/1549): \[java] NPE in PMD 6.8.0 InvalidSlf4jMessageFormat -* java-multithreading - * [#1533](https://github.com/pmd/pmd/issues/1533): \[java] New rule: UnsynchronizedStaticFormatter -* plsql - * [#1507](https://github.com/pmd/pmd/issues/1507): \[plsql] Parse Exception when using '||' operator in where clause - * [#1508](https://github.com/pmd/pmd/issues/1508): \[plsql] Parse Exception when using SELECT COUNT(\*) - * [#1509](https://github.com/pmd/pmd/issues/1509): \[plsql] Parse Exception with OUTER/INNER Joins - * [#1511](https://github.com/pmd/pmd/issues/1511): \[plsql] Parse Exception with IS NOT NULL - * [#1583](https://github.com/pmd/pmd/issues/1583): \[plsql] Update Set Clause should allow multiple columns - * [#1586](https://github.com/pmd/pmd/issues/1586): \[plsql] Parse Exception when functions are used with LIKE - ### API Changes -#### Deprecated API - -* {% jdoc core::lang.rule.stat.StatisticalRule %} and the related helper classes and base rule classes -are deprecated for removal in 7.0.0. This includes all of {% jdoc_package core::stat %} and {% jdoc_package core::lang.rule.stat %}, -and also {% jdoc java::lang.java.rule.AbstractStatisticalJavaRule %}, {% jdoc apex::lang.apex.rule.AbstractStatisticalApexRule %} and the like. -The methods {% jdoc !c!core::Report#addMetric(core::stat.Metric) %} and {% jdoc core::ThreadSafeReportListener#metricAdded(core::stat.Metric) %} -will also be removed. - - ### External Contributions -* [#1503](https://github.com/pmd/pmd/pull/1503): \[java] Fix for ReturnFromFinallyBlock false-positives - [RishabhDeep Singh](https://github.com/rishabhdeepsingh) -* [#1514](https://github.com/pmd/pmd/pull/1514): \[java] LocalVariableCouldBeFinal: allow excluding the variable in a for-each loop - [Kris Scheibe](https://github.com/kris-scheibe) -* [#1516](https://github.com/pmd/pmd/pull/1516): \[java] OneDeclarationPerLine: Don't report multiple variables in a for statement. - [Kris Scheibe](https://github.com/kris-scheibe) -* [#1520](https://github.com/pmd/pmd/pull/1520): \[java] New rule: ForLoopVariableCount: check the number of control variables in a for loop - [Kris Scheibe](https://github.com/kris-scheibe) -* [#1521](https://github.com/pmd/pmd/pull/1521): \[java] Upgrade to ASM7 for JDK 11 support - [Mark Pritchard](https://github.com/markpritchard) -* [#1530](https://github.com/pmd/pmd/pull/1530): \[java] New rule: AvoidReassigningLoopVariables - [Kris Scheibe](https://github.com/kris-scheibe) -* [#1534](https://github.com/pmd/pmd/pull/1534): \[java] This is the change regarding the usediamondoperator #1517 - [hemanshu070](https://github.com/hemanshu070) -* [#1545](https://github.com/pmd/pmd/pull/1545): \[doc] fixing dead links + tool to check for dead links automatically - [Kris Scheibe](https://github.com/kris-scheibe) -* [#1551](https://github.com/pmd/pmd/pull/1551): \[java] InvalidSlf4jMessageFormatRule should not throw NPE for enums - [Robbie Martinus](https://github.com/rmartinus) -* [#1552](https://github.com/pmd/pmd/pull/1552): \[core] Upgrading Google Gson from 2.5 to 2.8.5 - [Thunderforge](https://github.com/Thunderforge) -* [#1553](https://github.com/pmd/pmd/pull/1553): \[core] Upgrading System Rules dependency from 1.8.0 to 1.19.0 - [Thunderforge](https://github.com/Thunderforge) -* [#1554](https://github.com/pmd/pmd/pull/1554): \[plsql] updates should allow for multiple statements - [tashiscool](https://github.com/tashiscool) -* [#1584](https://github.com/pmd/pmd/pull/1584): \[core] Fixes 1196: inconsistencies of clones returned by different CPD executions for the same files - [Bruno Ferreira](https://github.com/bmbferreira) - {% endtocmaker %} diff --git a/docs/pages/release_notes_old.md b/docs/pages/release_notes_old.md index 15b232db0f..50e5f8e12e 100644 --- a/docs/pages/release_notes_old.md +++ b/docs/pages/release_notes_old.md @@ -5,6 +5,131 @@ permalink: pmd_release_notes_old.html Previous versions of PMD can be downloaded here: https://github.com/pmd/pmd/releases +## 27-January-2019 - 6.11.0 + +The PMD team is pleased to announce PMD 6.11.0. + +This is a minor release. + +### Table Of Contents + +* [New and noteworthy](#new-and-noteworthy) + * [Updated Apex Support](#updated-apex-support) + * [PL/SQL Grammar improvements](#pl/sql-grammar-improvements) + * [New Rules](#new-rules) + * [Modified Rules](#modified-rules) + * [Deprecated Rules](#deprecated-rules) +* [Fixed Issues](#fixed-issues) +* [API Changes](#api-changes) +* [External Contributions](#external-contributions) + +### New and noteworthy + +#### Updated Apex Support + +* The Apex language support has been bumped to version 45 (Spring '19). All new language features are now properly + parsed and processed. +* Many nodes now expose more informations, such as the operator for BooleanExpressions. This makes these operators + consumable by XPath rules, e.g. `//BooleanExpression[@Operator='&&']`. + +#### PL/SQL Grammar improvements + +* In this release, many parser bugs in our PL/SQL support have been fixed. This adds e.g. support for + table collection expressions (`SELECT * FROM TABLE(expr)`). +* Support for parsing insert statements has been added. +* More improvements are planned for the next release of PMD. + +#### New Rules + +* The new Java rule [`UnsynchronizedStaticFormatter`](https://pmd.github.io/pmd-6.11.0/pmd_rules_java_multithreading.html#unsynchronizedstaticformatter) (`java-multithreading`) detects + unsynchronized usages of static `java.text.Format` instances. This rule is a more generic replacement of the + rule [`UnsynchronizedStaticDateFormatter`](https://pmd.github.io/pmd-6.11.0/pmd_rules_java_multithreading.html#unsynchronizedstaticdateformatter) which focused just on `DateFormat`. + +* The new Java rule [`ForLoopVariableCount`](https://pmd.github.io/pmd-6.11.0/pmd_rules_java_bestpractices.html#forloopvariablecount) (`java-bestpractices`) checks for + the number of control variables in a for-loop. Having a lot of control variables makes it harder to understand + what the loop does. The maximum allowed number of variables is by default 1 and can be configured by a + property. + +* The new Java rule [`AvoidReassigningLoopVariables`](https://pmd.github.io/pmd-6.11.0/pmd_rules_java_bestpractices.html#avoidreassigningloopvariables) (`java-bestpractices`) searches + for loop variables that are reassigned. Changing the loop variables additionally to the loop itself can lead to + hard-to-find bugs. + +* The new Java rule [`UseDiamondOperator`](https://pmd.github.io/pmd-6.11.0/pmd_rules_java_codestyle.html#usediamondoperator) (`java-codestyle`) looks for constructor + calls with explicit type parameters. Since Java 1.7, these type parameters are not necessary anymore, as they + can be inferred now. + +#### Modified Rules + +* The Java rule [`LocalVariableCouldBeFinal`](https://pmd.github.io/pmd-6.11.0/pmd_rules_java_codestyle.html#localvariablecouldbefinal) (`java-codestyle`) has a new + property `ignoreForEachDecl`, which is by default disabled. The new property allows for ignoring + non-final loop variables in a for-each statement. + +#### Deprecated Rules + +* The Java rule [`UnsynchronizedStaticDateFormatter`](https://pmd.github.io/pmd-6.11.0/pmd_rules_java_multithreading.html#unsynchronizedstaticdateformatter) has been deprecated and + will be removed with PMD 7.0.0. The rule is replaced by the more general + [`UnsynchronizedStaticFormatter`](https://pmd.github.io/pmd-6.11.0/pmd_rules_java_multithreading.html#unsynchronizedstaticformatter). + +### Fixed Issues + +* core + * [#1196](https://github.com/pmd/pmd/issues/1196): \[core] CPD results not consistent between runs + * [#1496](https://github.com/pmd/pmd/issues/1496) \[core] Refactor metrics to be dealt with generically from pmd-core +* apex + * [#1542](https://github.com/pmd/pmd/pull/1542): \[apex] Include the documentation category + * [#1546](https://github.com/pmd/pmd/issues/1546): \[apex] PMD parsing exception for Apex classes using 'inherited sharing' keyword + * [#1568](https://github.com/pmd/pmd/pull/1568): \[apex] AST node attribute @Image not usable / always null in XPath rule / Designer +* java + * [#1556](https://github.com/pmd/pmd/issues/1556): \[java] Default methods should not be considered abstract + * [#1578](https://github.com/pmd/pmd/issues/1578): \[java] Private field is detected as public inside nested classes in interfaces +* java-bestpractices + * [#658](https://github.com/pmd/pmd/issues/658): \[java] OneDeclarationPerLine: False positive for loops + * [#1518](https://github.com/pmd/pmd/issues/1518): \[java] New rule: AvoidReassigningLoopVariable + * [#1519](https://github.com/pmd/pmd/issues/1519): \[java] New rule: ForLoopVariableCount +* java-codestyle + * [#1513](https://github.com/pmd/pmd/issues/1513): \[java] LocalVariableCouldBeFinal: allow excluding the variable in a for-each loop + * [#1517](https://github.com/pmd/pmd/issues/1517): \[java] New Rule: UseDiamondOperator +* java-errorprone + * [#1035](https://github.com/pmd/pmd/issues/1035): \[java] ReturnFromFinallyBlock: False positive on lambda expression in finally block + * [#1549](https://github.com/pmd/pmd/issues/1549): \[java] NPE in PMD 6.8.0 InvalidSlf4jMessageFormat +* java-multithreading + * [#1533](https://github.com/pmd/pmd/issues/1533): \[java] New rule: UnsynchronizedStaticFormatter +* plsql + * [#1507](https://github.com/pmd/pmd/issues/1507): \[plsql] Parse Exception when using '||' operator in where clause + * [#1508](https://github.com/pmd/pmd/issues/1508): \[plsql] Parse Exception when using SELECT COUNT(\*) + * [#1509](https://github.com/pmd/pmd/issues/1509): \[plsql] Parse Exception with OUTER/INNER Joins + * [#1511](https://github.com/pmd/pmd/issues/1511): \[plsql] Parse Exception with IS NOT NULL + * [#1526](https://github.com/pmd/pmd/issues/1526): \[plsql] ParseException when using TableCollectionExpression + * [#1583](https://github.com/pmd/pmd/issues/1583): \[plsql] Update Set Clause should allow multiple columns + * [#1586](https://github.com/pmd/pmd/issues/1586): \[plsql] Parse Exception when functions are used with LIKE + * [#1588](https://github.com/pmd/pmd/issues/1588): \[plsql] Parse Exception with function calls in WHERE clause + +### API Changes + +* [`StatisticalRule`](https://javadoc.io/page/net.sourceforge.pmd/pmd-core/6.11.0/net/sourceforge/pmd/lang/rule/stat/StatisticalRule.html#) and the related helper classes and base rule classes +are deprecated for removal in 7.0.0. This includes all of [`net.sourceforge.pmd.stat`](https://javadoc.io/page/net.sourceforge.pmd/pmd-core/6.11.0/net/sourceforge/pmd/stat/package-summary.html#) and [`net.sourceforge.pmd.lang.rule.stat`](https://javadoc.io/page/net.sourceforge.pmd/pmd-core/6.11.0/net/sourceforge/pmd/lang/rule/stat/package-summary.html#), +and also [`AbstractStatisticalJavaRule`](https://javadoc.io/page/net.sourceforge.pmd/pmd-java/6.11.0/net/sourceforge/pmd/lang/java/rule/AbstractStatisticalJavaRule.html#), [`AbstractStatisticalApexRule`](https://javadoc.io/page/net.sourceforge.pmd/pmd-apex/6.11.0/net/sourceforge/pmd/lang/apex/rule/AbstractStatisticalApexRule.html#) and the like. +The methods [`Report#addMetric`](https://javadoc.io/page/net.sourceforge.pmd/pmd-core/6.11.0/net/sourceforge/pmd/Report.html#addMetric(net.sourceforge.pmd.stat.Metric)) and [`metricAdded`](https://javadoc.io/page/net.sourceforge.pmd/pmd-core/6.11.0/net/sourceforge/pmd/ThreadSafeReportListener.html#metricAdded(net.sourceforge.pmd.stat.Metric)) +will also be removed. +* [`setProperty`](https://javadoc.io/page/net.sourceforge.pmd/pmd-core/6.11.0/net/sourceforge/pmd/properties/PropertySource.html#setProperty(net.sourceforge.pmd.properties.MultiValuePropertyDescriptor,Object[])) is deprecated, +because [`MultiValuePropertyDescriptor`](https://javadoc.io/page/net.sourceforge.pmd/pmd-core/6.11.0/net/sourceforge/pmd/properties/MultiValuePropertyDescriptor.html#) is deprecated as well + +### External Contributions + +* [#1503](https://github.com/pmd/pmd/pull/1503): \[java] Fix for ReturnFromFinallyBlock false-positives - [RishabhDeep Singh](https://github.com/rishabhdeepsingh) +* [#1514](https://github.com/pmd/pmd/pull/1514): \[java] LocalVariableCouldBeFinal: allow excluding the variable in a for-each loop - [Kris Scheibe](https://github.com/kris-scheibe) +* [#1516](https://github.com/pmd/pmd/pull/1516): \[java] OneDeclarationPerLine: Don't report multiple variables in a for statement. - [Kris Scheibe](https://github.com/kris-scheibe) +* [#1520](https://github.com/pmd/pmd/pull/1520): \[java] New rule: ForLoopVariableCount: check the number of control variables in a for loop - [Kris Scheibe](https://github.com/kris-scheibe) +* [#1521](https://github.com/pmd/pmd/pull/1521): \[java] Upgrade to ASM7 for JDK 11 support - [Mark Pritchard](https://github.com/markpritchard) +* [#1530](https://github.com/pmd/pmd/pull/1530): \[java] New rule: AvoidReassigningLoopVariables - [Kris Scheibe](https://github.com/kris-scheibe) +* [#1534](https://github.com/pmd/pmd/pull/1534): \[java] This is the change regarding the usediamondoperator #1517 - [hemanshu070](https://github.com/hemanshu070) +* [#1545](https://github.com/pmd/pmd/pull/1545): \[doc] fixing dead links + tool to check for dead links automatically - [Kris Scheibe](https://github.com/kris-scheibe) +* [#1551](https://github.com/pmd/pmd/pull/1551): \[java] InvalidSlf4jMessageFormatRule should not throw NPE for enums - [Robbie Martinus](https://github.com/rmartinus) +* [#1552](https://github.com/pmd/pmd/pull/1552): \[core] Upgrading Google Gson from 2.5 to 2.8.5 - [Thunderforge](https://github.com/Thunderforge) +* [#1553](https://github.com/pmd/pmd/pull/1553): \[core] Upgrading System Rules dependency from 1.8.0 to 1.19.0 - [Thunderforge](https://github.com/Thunderforge) +* [#1554](https://github.com/pmd/pmd/pull/1554): \[plsql] updates should allow for multiple statements - [tashiscool](https://github.com/tashiscool) +* [#1584](https://github.com/pmd/pmd/pull/1584): \[core] Fixes 1196: inconsistencies of clones returned by different CPD executions for the same files - [Bruno Ferreira](https://github.com/bmbferreira) + ## 09-December-2018 - 6.10.0 The PMD team is pleased to announce PMD 6.10.0. diff --git a/pmd-apex-jorje/pom.xml b/pmd-apex-jorje/pom.xml index 210bccf173..6d18f8f1a7 100644 --- a/pmd-apex-jorje/pom.xml +++ b/pmd-apex-jorje/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-apex/pom.xml b/pmd-apex/pom.xml index 4e2af0c24a..78754823fb 100644 --- a/pmd-apex/pom.xml +++ b/pmd-apex/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT 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 6984ff1a36..3a8663871d 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 @@ -5,21 +5,34 @@ package net.sourceforge.pmd.lang.apex; import java.io.Writer; +import java.util.Arrays; +import java.util.List; import net.sourceforge.pmd.lang.AbstractLanguageVersionHandler; import net.sourceforge.pmd.lang.Parser; import net.sourceforge.pmd.lang.ParserOptions; import net.sourceforge.pmd.lang.VisitorStarter; import net.sourceforge.pmd.lang.XPathHandler; +import net.sourceforge.pmd.lang.apex.ast.ASTMethod; +import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface; import net.sourceforge.pmd.lang.apex.ast.ApexNode; import net.sourceforge.pmd.lang.apex.ast.DumpFacade; +import net.sourceforge.pmd.lang.apex.metrics.ApexMetricsComputer; +import net.sourceforge.pmd.lang.apex.metrics.api.ApexClassMetricKey; +import net.sourceforge.pmd.lang.apex.metrics.api.ApexOperationMetricKey; import net.sourceforge.pmd.lang.apex.multifile.ApexMultifileVisitorFacade; import net.sourceforge.pmd.lang.apex.rule.ApexRuleViolationFactory; import net.sourceforge.pmd.lang.ast.xpath.DefaultASTXPathHandler; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; +import net.sourceforge.pmd.lang.metrics.internal.AbstractLanguageMetricsProvider; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; + public class ApexHandler extends AbstractLanguageVersionHandler { + private final ApexMetricsProvider myMetricsProvider = new ApexMetricsProvider(); + + @Override public VisitorStarter getMultifileFacade() { return rootNode -> new ApexMultifileVisitorFacade().initializeWith((ApexNode) rootNode); @@ -51,4 +64,31 @@ public class ApexHandler extends AbstractLanguageVersionHandler { return rootNode -> new DumpFacade().initializeWith(writer, prefix, recurse, (ApexNode) rootNode); } + + @Override + public LanguageMetricsProvider, ASTMethod> getLanguageMetricsProvider() { + return myMetricsProvider; + } + + + private static class ApexMetricsProvider extends AbstractLanguageMetricsProvider, ASTMethod> { + + @SuppressWarnings("unchecked") + ApexMetricsProvider() { + // a wild double cast + super((Class>) (Object) ASTUserClassOrInterface.class, ASTMethod.class, ApexMetricsComputer.getInstance()); + } + + + @Override + public List getAvailableTypeMetrics() { + return Arrays.asList(ApexClassMetricKey.values()); + } + + + @Override + public List getAvailableOperationMetrics() { + return Arrays.asList(ApexOperationMetricKey.values()); + } + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTAssignmentExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTAssignmentExpression.java index 29f466692b..de3531be71 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTAssignmentExpression.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTAssignmentExpression.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.apex.ast; +import apex.jorje.data.ast.AssignmentOp; import apex.jorje.semantic.ast.expression.AssignmentExpression; public class ASTAssignmentExpression extends AbstractApexNode { @@ -16,4 +17,8 @@ public class ASTAssignmentExpression extends AbstractApexNode { @@ -16,4 +17,8 @@ public class ASTBinaryExpression extends AbstractApexNode { public Object jjtAccept(ApexParserVisitor visitor, Object data) { return visitor.visit(this, data); } + + public BinaryOp getOperator() { + return node.getOp(); + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTBooleanExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTBooleanExpression.java index f2e44dbc4a..efd46f2a2b 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTBooleanExpression.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTBooleanExpression.java @@ -4,16 +4,25 @@ package net.sourceforge.pmd.lang.apex.ast; +import apex.jorje.data.ast.BooleanOp; import apex.jorje.semantic.ast.expression.BooleanExpression; + public class ASTBooleanExpression extends AbstractApexNode { public ASTBooleanExpression(BooleanExpression booleanExpression) { super(booleanExpression); } + @Override public Object jjtAccept(ApexParserVisitor visitor, Object data) { return visitor.visit(this, data); } + + + public BooleanOp getOperator() { + return this.node.getOp(); + } + } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTLiteralExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTLiteralExpression.java index 19e7440fac..35b04e7d37 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTLiteralExpression.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTLiteralExpression.java @@ -4,16 +4,24 @@ package net.sourceforge.pmd.lang.apex.ast; +import apex.jorje.data.ast.LiteralType; import apex.jorje.semantic.ast.expression.LiteralExpression; + public class ASTLiteralExpression extends AbstractApexNode { public ASTLiteralExpression(LiteralExpression literalExpression) { super(literalExpression); } + @Override public Object jjtAccept(ApexParserVisitor visitor, Object data) { return visitor.visit(this, data); } + + + public LiteralType getLiteralType() { + return node.getLiteralType(); + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTPostfixExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTPostfixExpression.java index 4d2bd2b017..5a760da8b2 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTPostfixExpression.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTPostfixExpression.java @@ -4,16 +4,24 @@ package net.sourceforge.pmd.lang.apex.ast; +import apex.jorje.data.ast.PostfixOp; import apex.jorje.semantic.ast.expression.PostfixExpression; + public class ASTPostfixExpression extends AbstractApexNode { public ASTPostfixExpression(PostfixExpression postfixExpression) { super(postfixExpression); } + @Override public Object jjtAccept(ApexParserVisitor visitor, Object data) { return visitor.visit(this, data); } + + + public PostfixOp getOperator() { + return node.getOp(); + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTPrefixExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTPrefixExpression.java index 11db2fb4a3..4dd4a20cde 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTPrefixExpression.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTPrefixExpression.java @@ -4,6 +4,7 @@ package net.sourceforge.pmd.lang.apex.ast; +import apex.jorje.data.ast.PrefixOp; import apex.jorje.semantic.ast.expression.PrefixExpression; public class ASTPrefixExpression extends AbstractApexNode { @@ -16,4 +17,10 @@ public class ASTPrefixExpression extends AbstractApexNode { public Object jjtAccept(ApexParserVisitor visitor, Object data) { return visitor.visit(this, data); } + + + public PrefixOp getOperator() { + return node.getOp(); + } + } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTReferenceExpression.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTReferenceExpression.java index 32e91d26bb..ef3db237a3 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTReferenceExpression.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTReferenceExpression.java @@ -4,7 +4,10 @@ package net.sourceforge.pmd.lang.apex.ast; +import apex.jorje.semantic.ast.expression.IdentifierContext; import apex.jorje.semantic.ast.expression.ReferenceExpression; +import apex.jorje.semantic.ast.expression.ReferenceType; + public class ASTReferenceExpression extends AbstractApexNode { @@ -12,8 +15,19 @@ public class ASTReferenceExpression extends AbstractApexNode extends AbstractApexNo protected final T node; - public AbstractApexNode(T node) { + protected AbstractApexNode(T node) { super(node.getClass()); this.node = node; } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsComputer.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsComputer.java index d0d82c3d63..63ccc76f74 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsComputer.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsComputer.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.apex.metrics; import java.util.ArrayList; import java.util.List; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.apex.ast.ASTMethod; import net.sourceforge.pmd.lang.apex.ast.ASTUserClassOrInterface; import net.sourceforge.pmd.lang.metrics.AbstractMetricsComputer; @@ -18,7 +19,13 @@ import net.sourceforge.pmd.lang.metrics.AbstractMetricsComputer; */ public class ApexMetricsComputer extends AbstractMetricsComputer, ASTMethod> { - static final ApexMetricsComputer INSTANCE = new ApexMetricsComputer(); + private static final ApexMetricsComputer INSTANCE = new ApexMetricsComputer(); + + + @InternalApi + public static ApexMetricsComputer getInstance() { + return INSTANCE; + } @Override diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsFacade.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsFacade.java index 0bcd271ab9..4ed4fa23d3 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsFacade.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/metrics/ApexMetricsFacade.java @@ -27,7 +27,7 @@ public class ApexMetricsFacade extends AbstractMetricsFacade, ASTMethod> getLanguageSpecificComputer() { - return ApexMetricsComputer.INSTANCE; + return ApexMetricsComputer.getInstance(); } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserXPathTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserXPathTest.java new file mode 100644 index 0000000000..a7b1f2012d --- /dev/null +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexParserXPathTest.java @@ -0,0 +1,35 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.apex.ast; + +import static net.sourceforge.pmd.lang.apex.ast.ApexParserTestHelpers.parse; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.lang.ast.Node; + +import apex.jorje.semantic.ast.compilation.Compilation; + +public class ApexParserXPathTest { + + @Test + public void testBooleanExpressions() throws Exception { + ApexNode node = parse(IOUtils.toString(ApexParserXPathTest.class.getResourceAsStream("BooleanExpressions.cls"), + StandardCharsets.UTF_8)); + List booleanExpressions = node.findDescendantsOfType(ASTBooleanExpression.class); + Assert.assertEquals(2, booleanExpressions.size()); + Assert.assertEquals("&&", booleanExpressions.get(0).getOperator().toString()); + Assert.assertEquals("!=", booleanExpressions.get(1).getOperator().toString()); + + List xpathResult = node.findChildNodesWithXPath("//BooleanExpression[@Operator='&&']"); + Assert.assertEquals(1, xpathResult.size()); + Assert.assertSame(booleanExpressions.get(0), xpathResult.get(0)); + } +} diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/metrics/ApexProjectMirrorTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/metrics/ApexProjectMirrorTest.java index 9c78229005..49d077318b 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/metrics/ApexProjectMirrorTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/metrics/ApexProjectMirrorTest.java @@ -91,7 +91,7 @@ public class ApexProjectMirrorTest { @Override public Object visit(ASTMethod node, Object data) { MetricMemoizer op = toplevel.getOperationMemoizer(node.getQualifiedName()); - result.add((int) ApexMetricsComputer.INSTANCE.computeForOperation(opMetricKey, node, force, + result.add((int) ApexMetricsComputer.getInstance().computeForOperation(opMetricKey, node, force, MetricOptions.emptyOptions(), op)); return super.visit(node, data); } @@ -100,7 +100,7 @@ public class ApexProjectMirrorTest { @Override public Object visit(ASTUserClass node, Object data) { MetricMemoizer> clazz = toplevel.getClassMemoizer(node.getQualifiedName()); - result.add((int) ApexMetricsComputer.INSTANCE.computeForType(classMetricKey, node, force, + result.add((int) ApexMetricsComputer.getInstance().computeForType(classMetricKey, node, force, MetricOptions.emptyOptions(), clazz)); return super.visit(node, data); } diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/BooleanExpressions.cls b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/BooleanExpressions.cls new file mode 100644 index 0000000000..d70885a456 --- /dev/null +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/BooleanExpressions.cls @@ -0,0 +1,9 @@ +// See https://github.com/pmd/pmd/issues/1568 + +class MyApexClass { + void bar(){ + if(!alist.isEmpty() && alist != null) { + foo(); + } + } +} diff --git a/pmd-core/pom.xml b/pmd-core/pom.xml index 3e3c6f51fc..1f7f28b889 100644 --- a/pmd-core/pom.xml +++ b/pmd-core/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java new file mode 100644 index 0000000000..b7ae345175 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/internal/util/IteratorUtil.java @@ -0,0 +1,49 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.internal.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + + +/** + * @author Clément Fournier + * @since 6.11.0 + */ +public final class IteratorUtil { + + private IteratorUtil() { + + } + + + public static Iterator reverse(Iterator it) { + List tmp = toList(it); + Collections.reverse(tmp); + return tmp.iterator(); + } + + + public static List toList(Iterator it) { + List list = new ArrayList<>(); + while (it.hasNext()) { + list.add(it.next()); + } + return list; + } + + + public static Iterable toIterable(final Iterator it) { + return new Iterable() { + @Override + public Iterator iterator() { + return it; + } + }; + } + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractLanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractLanguageVersionHandler.java index 400be33c40..c00f38a05f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractLanguageVersionHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractLanguageVersionHandler.java @@ -7,6 +7,8 @@ package net.sourceforge.pmd.lang; import java.io.Writer; import net.sourceforge.pmd.lang.dfa.DFAGraphRule; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; + /** * This is a generic implementation of the LanguageVersionHandler interface. @@ -71,4 +73,10 @@ public abstract class AbstractLanguageVersionHandler implements LanguageVersionH public DFAGraphRule getDFAGraphRule() { return null; } + + + @Override + public LanguageMetricsProvider getLanguageMetricsProvider() { + return null; + } } 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 5037c9f456..5474b1758a 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 @@ -6,8 +6,10 @@ package net.sourceforge.pmd.lang; import java.io.Writer; +import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.dfa.DFAGraphRule; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; /** @@ -135,4 +137,17 @@ public interface LanguageVersionHandler { @Deprecated @InternalApi DFAGraphRule getDFAGraphRule(); + + + /** + * Returns the metrics provider for this language version, + * or null if it has none. + * + * Note: this is experimental, ie unstable until 7.0.0, after + * which it will probably be promoted to a stable API. For + * instance the return type will probably be changed to an Optional. + */ + @Experimental + LanguageMetricsProvider getLanguageMetricsProvider(); + } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java index 6f57f00dab..ef05342008 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/Attribute.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.ast.xpath; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.logging.Level; @@ -18,6 +19,9 @@ import net.sourceforge.pmd.lang.ast.Node; * Attributes know their name, the node they wrap, * and have access to their value. * + *

Two attributes are equal if they have the same name + * and their parent nodes are equal. + * * @author daniels */ public class Attribute { @@ -28,8 +32,8 @@ public class Attribute { private static final Object[] EMPTY_OBJ_ARRAY = new Object[0]; - private Node parent; - private String name; + private final Node parent; + private final String name; private Method method; private Object value; private String stringValue; @@ -41,7 +45,6 @@ public class Attribute { this.method = m; } - /** Creates a new attribute belonging to the given node using its string value. */ public Attribute(Node parent, String name, String value) { this.parent = parent; @@ -93,12 +96,33 @@ public class Attribute { return stringValue; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Attribute attribute = (Attribute) o; + return Objects.equals(parent, attribute.parent) + && Objects.equals(name, attribute.name); + } + + + @Override + public int hashCode() { + return Objects.hash(parent, name); + } + + private String getLoggableAttributeName() { return parent.getXPathNodeName() + "/@" + name; } @Override public String toString() { - return name + ':' + getValue() + ':' + parent; + return name + ':' + getValue() + ':' + parent.getXPathNodeName(); } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIterator.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIterator.java index 75807ecd71..13397c16e1 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIterator.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIterator.java @@ -69,7 +69,6 @@ public class AttributeAxisIterator implements Iterator { this.currObj = getNextAttribute(); } - /** * Returns whether the given method is an attribute accessor, * in which case a corresponding Attribute will be added to @@ -80,12 +79,16 @@ public class AttributeAxisIterator implements Iterator { protected boolean isAttributeAccessor(Method method) { String methodName = method.getName(); - return CONSIDERED_RETURN_TYPES.contains(method.getReturnType()) + return isConsideredReturnType(method.getReturnType()) && method.getParameterTypes().length == 0 && !methodName.startsWith("jjt") && !FILTERED_OUT_NAMES.contains(methodName); } + private boolean isConsideredReturnType(Class klass) { + return CONSIDERED_RETURN_TYPES.contains(klass) || klass.isEnum(); + } + @Override public Attribute next() { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/LanguageMetricsProvider.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/LanguageMetricsProvider.java new file mode 100644 index 0000000000..ec57cceff9 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/LanguageMetricsProvider.java @@ -0,0 +1,92 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.metrics; + +import java.util.List; +import java.util.Map; + +import net.sourceforge.pmd.annotation.Experimental; +import net.sourceforge.pmd.lang.LanguageVersionHandler; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.QualifiableNode; + + +/** + * Language-specific provider for metrics. Knows about all the metrics + * defined for a language. Can be used e.g. to build GUI applications + * like the designer, in a language independent way. Accessible through + * {@link LanguageVersionHandler#getLanguageMetricsProvider()}. + * + * Note: this is experimental, ie unstable until 7.0.0, after which it will probably + * be promoted to a real API. + * + * @param Type of type declaration nodes of the language + * @param Type of operation declaration nodes of the language + * + * @author Clément Fournier + * @since 6.11.0 + */ +@Experimental +public interface LanguageMetricsProvider { + + /** + * Returns a list of all supported type metric keys + * for the language. + */ + List> getAvailableTypeMetrics(); + + + /** + * Returns a list of all supported operation metric keys + * for the language. + */ + List> getAvailableOperationMetrics(); + + + /** + * Returns the given node casted to {@link T} if it's of the correct + * type, otherwise returns null. + */ + T asTypeNode(Node anyNode); + + + /** + * Returns the given node casted to {@link O} if it's of the correct + * type, otherwise returns null. + */ + O asOperationNode(Node anyNode); + + + /** + * Like {@link MetricsComputer#computeForType(MetricKey, QualifiableNode, boolean, MetricOptions, MetricMemoizer)}, + * but performs no memoisation. + */ + double computeForType(MetricKey key, T node, MetricOptions options); + + + /** + * Like {@link MetricsComputer#computeForOperation(MetricKey, QualifiableNode, boolean, MetricOptions, MetricMemoizer)} + * but performs no memoisation. + */ + double computeForOperation(MetricKey key, O node, MetricOptions options); + + + /** + * Like {@link MetricsComputer#computeWithResultOption(MetricKey, QualifiableNode, boolean, MetricOptions, ResultOption, ProjectMemoizer)} + * but performs no memoisation. + */ + double computeWithResultOption(MetricKey key, T node, MetricOptions options, ResultOption option); + + + /** + * Computes all metrics available on the given node. + * The returned results may contain Double.NaN as a value. + * + * @param node Node to inspect + * + * @return A map of metric key to their result, possibly empty, but with no null value + */ + Map, Double> computeAllMetricsFor(Node node); +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/MetricKey.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/MetricKey.java index 4f7521df0d..2259ed8956 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/MetricKey.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/MetricKey.java @@ -41,5 +41,6 @@ public interface MetricKey { */ boolean supports(N node); + // TODO the metric key should know about supported options } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/AbstractLanguageMetricsProvider.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/AbstractLanguageMetricsProvider.java new file mode 100644 index 0000000000..d6f46e0d7a --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/AbstractLanguageMetricsProvider.java @@ -0,0 +1,90 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.metrics.internal; + +import java.util.HashMap; +import java.util.Map; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.ast.QualifiableNode; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; +import net.sourceforge.pmd.lang.metrics.MetricKey; +import net.sourceforge.pmd.lang.metrics.MetricOptions; +import net.sourceforge.pmd.lang.metrics.MetricsComputer; +import net.sourceforge.pmd.lang.metrics.ResultOption; + + +/** + * Base implementation for {@link LanguageMetricsProvider}. + * + * @author Clément Fournier + * @since 6.11.0 + */ +public abstract class AbstractLanguageMetricsProvider implements LanguageMetricsProvider { + + private final Class tClass; + private final Class oClass; + private final MetricsComputer myComputer; + + + protected AbstractLanguageMetricsProvider(Class tClass, + Class oClass, + MetricsComputer computer) { + this.tClass = tClass; + this.oClass = oClass; + this.myComputer = computer; + } + + + @Override + public T asTypeNode(Node anyNode) { + return tClass.isInstance(anyNode) ? tClass.cast(anyNode) : null; + } + + + @Override + public O asOperationNode(Node anyNode) { + return oClass.isInstance(anyNode) ? oClass.cast(anyNode) : null; + } + + + @Override + public double computeForType(MetricKey key, T node, MetricOptions options) { + return myComputer.computeForType(key, node, true, options, DummyMetricMemoizer.getInstance()); + } + + + @Override + public double computeForOperation(MetricKey key, O node, MetricOptions options) { + return myComputer.computeForOperation(key, node, true, options, DummyMetricMemoizer.getInstance()); + } + + + @Override + public double computeWithResultOption(MetricKey key, T node, MetricOptions options, ResultOption option) { + return myComputer.computeWithResultOption(key, node, true, options, option, DummyProjectMemoizer.getInstance()); + } + + + @Override + public Map, Double> computeAllMetricsFor(Node node) { + Map, Double> results = new HashMap<>(); + T t = asTypeNode(node); + if (t != null) { + for (MetricKey tkey : getAvailableTypeMetrics()) { + results.put(tkey, computeForType(tkey, t, MetricOptions.emptyOptions())); + } + } + O o = asOperationNode(node); + if (o != null) { + for (MetricKey okey : getAvailableOperationMetrics()) { + results.put(okey, computeForOperation(okey, o, MetricOptions.emptyOptions())); + } + } + + return results; + } + +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/DummyMetricMemoizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/DummyMetricMemoizer.java new file mode 100644 index 0000000000..1d6cae45a1 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/DummyMetricMemoizer.java @@ -0,0 +1,44 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.metrics.internal; + +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.metrics.MetricMemoizer; +import net.sourceforge.pmd.lang.metrics.ParameterizedMetricKey; + + +/** + * Memoizes nothing. + * + * @author Clément Fournier + * @since 6.11.0 + */ +public final class DummyMetricMemoizer implements MetricMemoizer { + + private static final DummyMetricMemoizer INSTANCE = new DummyMetricMemoizer<>(); + + + private DummyMetricMemoizer() { + + } + + + @Override + public Double getMemo(ParameterizedMetricKey key) { + return null; + } + + + @Override + public void memoize(ParameterizedMetricKey key, double value) { + // do nothing + } + + + @SuppressWarnings("unchecked") + public static DummyMetricMemoizer getInstance() { + return (DummyMetricMemoizer) INSTANCE; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/DummyProjectMemoizer.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/DummyProjectMemoizer.java new file mode 100644 index 0000000000..10abe75a9e --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/metrics/internal/DummyProjectMemoizer.java @@ -0,0 +1,45 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.metrics.internal; + +import net.sourceforge.pmd.lang.ast.QualifiableNode; +import net.sourceforge.pmd.lang.ast.QualifiedName; +import net.sourceforge.pmd.lang.metrics.MetricMemoizer; +import net.sourceforge.pmd.lang.metrics.ProjectMemoizer; + + +/** + * Memoizes nothing. + * + * @author Clément Fournier + * @since 6.11.0 + */ +public final class DummyProjectMemoizer implements ProjectMemoizer { + + private static final DummyProjectMemoizer INSTANCE = new DummyProjectMemoizer<>(); + + + private DummyProjectMemoizer() { + + } + + + @Override + public MetricMemoizer getOperationMemoizer(QualifiedName qname) { + return DummyMetricMemoizer.getInstance(); + } + + + @Override + public MetricMemoizer getClassMemoizer(QualifiedName qname) { + return DummyMetricMemoizer.getInstance(); + } + + + @SuppressWarnings("unchecked") + public static DummyProjectMemoizer getInstance() { + return (DummyProjectMemoizer) INSTANCE; + } +} diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java index ddbe17615b..b369007c68 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/xpath/SaxonXPathRuleQuery.java @@ -236,7 +236,9 @@ public class SaxonXPathRuleQuery extends AbstractXPathRuleQuery { */ if (value == null) { return UntypedAtomicValue.ZERO_LENGTH_UNTYPED; - + } else if (value instanceof Enum) { + // enums use their toString + return new StringValue(value.toString()); } else if (value instanceof String) { return new StringValue((String) value); } else if (value instanceof Boolean) { 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 386837f0a9..71463940cf 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 @@ -164,6 +164,7 @@ public abstract class AbstractPropertySource implements PropertySource { @Override + @Deprecated public void setProperty(MultiValuePropertyDescriptor propertyDescriptor, V... values) { checkValidPropertyDescriptor(propertyDescriptor); propertyValuesByDescriptor.put(propertyDescriptor, Collections.unmodifiableList(Arrays.asList(values))); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java index 7b24644164..7a516714bd 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/PropertySource.java @@ -112,7 +112,10 @@ public interface PropertySource { * @param propertyDescriptor The property descriptor for which to add a value * @param values Values * @param The type of the values + * + * @deprecated {@link MultiValuePropertyDescriptor} is deprecated */ + @Deprecated void setProperty(MultiValuePropertyDescriptor propertyDescriptor, V... values); diff --git a/pmd-core/src/main/resources/rulesets/releases/6110.xml b/pmd-core/src/main/resources/rulesets/releases/6110.xml new file mode 100644 index 0000000000..f0479f4339 --- /dev/null +++ b/pmd-core/src/main/resources/rulesets/releases/6110.xml @@ -0,0 +1,17 @@ + + + + +This ruleset contains links to rules that are new in PMD v6.11.0 + + + + + + + + + diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIteratorTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIteratorTest.java index e8326ca32d..4827e8f544 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIteratorTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/ast/xpath/AttributeAxisIteratorTest.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.ast.xpath; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.util.HashMap; @@ -52,6 +53,17 @@ public class AttributeAxisIteratorTest { assertTrue(atts.containsKey("EndLine")); } + @Test + public void testAttributeAxisIteratorWithEnum() { + DummyNodeWithEnum dummyNode = new DummyNodeWithEnum(1); + + AttributeAxisIterator it = new AttributeAxisIterator(dummyNode); + Map atts = toMap(it); + Assert.assertEquals(8, atts.size()); + assertTrue(atts.containsKey("Enum")); + assertEquals(DummyNodeWithEnum.MyEnum.FOO, atts.get("Enum").getValue()); + } + private Map toMap(AttributeAxisIterator it) { Map atts = new HashMap<>(); @@ -61,4 +73,19 @@ public class AttributeAxisIteratorTest { } return atts; } + + public static class DummyNodeWithEnum extends DummyNode { + + public DummyNodeWithEnum(int id) { + super(id); + } + + public enum MyEnum { + FOO, BAR + } + + public MyEnum getEnum() { + return MyEnum.FOO; + } + } } diff --git a/pmd-cpp/pom.xml b/pmd-cpp/pom.xml index 0552cc11f7..5d0afa82cb 100644 --- a/pmd-cpp/pom.xml +++ b/pmd-cpp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-cs/pom.xml b/pmd-cs/pom.xml index 8cb50f99fe..d915d20c47 100644 --- a/pmd-cs/pom.xml +++ b/pmd-cs/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-dist/pom.xml b/pmd-dist/pom.xml index d234675496..0aa8302284 100644 --- a/pmd-dist/pom.xml +++ b/pmd-dist/pom.xml @@ -8,11 +8,36 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT + + pmd-bin-${project.version} + + + + maven-resources-plugin + + + copy-resources + prepare-package + + copy-resources + + + ${basedir}/target/extra-resources + + + src/main/resources + false + + + + + + maven-assembly-plugin @@ -30,9 +55,9 @@ single - pmd-bin-${project.version} + ${pmd.dist.bin.baseDirectory} - src/main/assembly/bin.xml + src/main/resources/assemblies/pmd-bin.xml @@ -45,7 +70,7 @@ pmd-src-${project.version} - src/main/assembly/src.xml + src/main/resources/assemblies/pmd-src.xml diff --git a/pmd-dist/LICENSE b/pmd-dist/src/main/resources/LICENSE similarity index 100% rename from pmd-dist/LICENSE rename to pmd-dist/src/main/resources/LICENSE diff --git a/pmd-dist/src/main/assembly/bin.xml b/pmd-dist/src/main/resources/assemblies/pmd-bin.xml similarity index 84% rename from pmd-dist/src/main/assembly/bin.xml rename to pmd-dist/src/main/resources/assemblies/pmd-bin.xml index 2b44241aa9..8b0c0134b2 100644 --- a/pmd-dist/src/main/assembly/bin.xml +++ b/pmd-dist/src/main/resources/assemblies/pmd-bin.xml @@ -1,13 +1,13 @@ - bin + pmd-bin zip true - pmd-bin-${project.version} + ${pmd.dist.bin.baseDirectory} @@ -18,7 +18,7 @@ designer.bat pmd.bat - src/main/scripts + target/extra-resources/scripts bin 0755 0755 @@ -29,7 +29,7 @@ run.sh - src/main/scripts + target/extra-resources/scripts bin 0755 0755 @@ -40,6 +40,8 @@ LICENSE + target/extra-resources + . 0755 0644 diff --git a/pmd-dist/src/main/assembly/src.xml b/pmd-dist/src/main/resources/assemblies/pmd-src.xml similarity index 98% rename from pmd-dist/src/main/assembly/src.xml rename to pmd-dist/src/main/resources/assemblies/pmd-src.xml index e69362fffc..8c8820d03c 100644 --- a/pmd-dist/src/main/assembly/src.xml +++ b/pmd-dist/src/main/resources/assemblies/pmd-src.xml @@ -1,7 +1,7 @@ - src + pmd-src zip diff --git a/pmd-dist/src/main/scripts/bgastviewer.bat b/pmd-dist/src/main/resources/scripts/bgastviewer.bat similarity index 100% rename from pmd-dist/src/main/scripts/bgastviewer.bat rename to pmd-dist/src/main/resources/scripts/bgastviewer.bat diff --git a/pmd-dist/src/main/scripts/cpd.bat b/pmd-dist/src/main/resources/scripts/cpd.bat similarity index 100% rename from pmd-dist/src/main/scripts/cpd.bat rename to pmd-dist/src/main/resources/scripts/cpd.bat diff --git a/pmd-dist/src/main/scripts/cpdgui.bat b/pmd-dist/src/main/resources/scripts/cpdgui.bat similarity index 100% rename from pmd-dist/src/main/scripts/cpdgui.bat rename to pmd-dist/src/main/resources/scripts/cpdgui.bat diff --git a/pmd-dist/src/main/scripts/designer.bat b/pmd-dist/src/main/resources/scripts/designer.bat similarity index 100% rename from pmd-dist/src/main/scripts/designer.bat rename to pmd-dist/src/main/resources/scripts/designer.bat diff --git a/pmd-dist/src/main/scripts/designerold.bat b/pmd-dist/src/main/resources/scripts/designerold.bat similarity index 100% rename from pmd-dist/src/main/scripts/designerold.bat rename to pmd-dist/src/main/resources/scripts/designerold.bat diff --git a/pmd-dist/src/main/scripts/pmd.bat b/pmd-dist/src/main/resources/scripts/pmd.bat similarity index 100% rename from pmd-dist/src/main/scripts/pmd.bat rename to pmd-dist/src/main/resources/scripts/pmd.bat diff --git a/pmd-dist/src/main/scripts/run.sh b/pmd-dist/src/main/resources/scripts/run.sh similarity index 100% rename from pmd-dist/src/main/scripts/run.sh rename to pmd-dist/src/main/resources/scripts/run.sh 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 fd1c66249f..b37093098f 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 @@ -5,6 +5,7 @@ package net.sourceforge.pmd.it; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; @@ -59,6 +60,7 @@ public class BinaryDistributionIT { Set result = new HashSet<>(); String basedir = "pmd-bin-" + PMDVersion.VERSION + "/"; result.add(basedir); + result.add(basedir + "LICENSE"); result.add(basedir + "bin/run.sh"); result.add(basedir + "bin/pmd.bat"); result.add(basedir + "bin/cpd.bat"); @@ -81,7 +83,9 @@ public class BinaryDistributionIT { zip.close(); - assertTrue(expectedFileNames.isEmpty()); + if (!expectedFileNames.isEmpty()) { + fail("Missing files in archive: " + expectedFileNames); + } } @Test diff --git a/pmd-doc/pom.xml b/pmd-doc/pom.xml index 0f56881346..837214c7bb 100644 --- a/pmd-doc/pom.xml +++ b/pmd-doc/pom.xml @@ -8,7 +8,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-fortran/pom.xml b/pmd-fortran/pom.xml index 95882d573b..15f0f0568b 100644 --- a/pmd-fortran/pom.xml +++ b/pmd-fortran/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-go/pom.xml b/pmd-go/pom.xml index 4575e509ab..59c3390927 100644 --- a/pmd-go/pom.xml +++ b/pmd-go/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-groovy/pom.xml b/pmd-groovy/pom.xml index 34353d2920..66f24166be 100644 --- a/pmd-groovy/pom.xml +++ b/pmd-groovy/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-java/pom.xml b/pmd-java/pom.xml index 654e3602d7..ed1c2b3675 100644 --- a/pmd-java/pom.xml +++ b/pmd-java/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/AbstractJavaHandler.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/AbstractJavaHandler.java index 725a4aae03..179ac6a6f1 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/AbstractJavaHandler.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/AbstractJavaHandler.java @@ -5,6 +5,8 @@ package net.sourceforge.pmd.lang.java; import java.io.Writer; +import java.util.Arrays; +import java.util.List; import net.sourceforge.pmd.lang.AbstractLanguageVersionHandler; import net.sourceforge.pmd.lang.DataFlowHandler; @@ -14,11 +16,16 @@ import net.sourceforge.pmd.lang.XPathHandler; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.xpath.DefaultASTXPathHandler; import net.sourceforge.pmd.lang.dfa.DFAGraphRule; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.DumpFacade; import net.sourceforge.pmd.lang.java.ast.JavaNode; +import net.sourceforge.pmd.lang.java.ast.MethodLikeNode; import net.sourceforge.pmd.lang.java.dfa.DataFlowFacade; import net.sourceforge.pmd.lang.java.dfa.JavaDFAGraphRule; +import net.sourceforge.pmd.lang.java.metrics.JavaMetricsComputer; +import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey; +import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey; import net.sourceforge.pmd.lang.java.multifile.MultifileVisitorFacade; import net.sourceforge.pmd.lang.java.qname.QualifiedNameResolver; import net.sourceforge.pmd.lang.java.rule.JavaRuleViolationFactory; @@ -30,6 +37,9 @@ import net.sourceforge.pmd.lang.java.xpath.MetricFunction; import net.sourceforge.pmd.lang.java.xpath.TypeIsExactlyFunction; import net.sourceforge.pmd.lang.java.xpath.TypeIsFunction; import net.sourceforge.pmd.lang.java.xpath.TypeOfFunction; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; +import net.sourceforge.pmd.lang.metrics.MetricKey; +import net.sourceforge.pmd.lang.metrics.internal.AbstractLanguageMetricsProvider; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; import net.sf.saxon.sxpath.IndependentContext; @@ -42,6 +52,8 @@ import net.sf.saxon.sxpath.IndependentContext; */ public abstract class AbstractJavaHandler extends AbstractLanguageVersionHandler { + private final LanguageMetricsProvider myMetricsProvider = new JavaMetricsProvider(); + @Override public DataFlowHandler getDataFlowHandler() { return new JavaDataFlowHandler(); @@ -147,4 +159,31 @@ public abstract class AbstractJavaHandler extends AbstractLanguageVersionHandler public DFAGraphRule getDFAGraphRule() { return new JavaDFAGraphRule(); } + + + @Override + public LanguageMetricsProvider getLanguageMetricsProvider() { + return myMetricsProvider; + } + + + private static class JavaMetricsProvider extends AbstractLanguageMetricsProvider { + + + JavaMetricsProvider() { + super(ASTAnyTypeDeclaration.class, MethodLikeNode.class, JavaMetricsComputer.getInstance()); + } + + + @Override + public List> getAvailableTypeMetrics() { + return Arrays.asList(JavaClassMetricKey.values()); + } + + + @Override + public List> getAvailableOperationMetrics() { + return Arrays.asList(JavaOperationMetricKey.values()); + } + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsComputer.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsComputer.java index b96d66b93a..9d95cb4eda 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsComputer.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsComputer.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang.java.metrics; import java.util.ArrayList; import java.util.List; +import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeBodyDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodOrConstructorDeclaration; @@ -20,12 +21,19 @@ import net.sourceforge.pmd.lang.metrics.AbstractMetricsComputer; */ public final class JavaMetricsComputer extends AbstractMetricsComputer { - static final JavaMetricsComputer INSTANCE = new JavaMetricsComputer(); + private static final JavaMetricsComputer INSTANCE = new JavaMetricsComputer(); private JavaMetricsComputer() { } + + @InternalApi + public static JavaMetricsComputer getInstance() { + return INSTANCE; + } + + // TODO: doesn't consider lambdas @Override protected List findOperations(ASTAnyTypeDeclaration node) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsFacade.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsFacade.java index 6ddd2e1df9..f239407db7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsFacade.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsFacade.java @@ -33,7 +33,7 @@ class JavaMetricsFacade extends AbstractMetricsFacade getLanguageSpecificComputer() { - return JavaMetricsComputer.INSTANCE; + return JavaMetricsComputer.getInstance(); } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/api/JavaClassMetricKey.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/api/JavaClassMetricKey.java index 2d51621aa1..06ff9ed917 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/api/JavaClassMetricKey.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/metrics/api/JavaClassMetricKey.java @@ -96,5 +96,4 @@ public enum JavaClassMetricKey implements MetricKey { return calculator.supports(node); } - } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ParserTstUtil.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ParserTstUtil.java index cd3064565d..15352e90d1 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ParserTstUtil.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/ParserTstUtil.java @@ -205,12 +205,12 @@ public class ParserTstUtil { } - public static LanguageVersionHandler getLanguageVersionHandler(String version) { - return LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion(version).getLanguageVersionHandler(); + public static AbstractJavaHandler getLanguageVersionHandler(String version) { + return (AbstractJavaHandler) LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getVersion(version).getLanguageVersionHandler(); } - public static LanguageVersionHandler getDefaultLanguageVersionHandler() { - return LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getDefaultVersion().getLanguageVersionHandler(); + public static AbstractJavaHandler getDefaultLanguageVersionHandler() { + return (AbstractJavaHandler) LanguageRegistry.getLanguage(JavaLanguageModule.NAME).getDefaultVersion().getLanguageVersionHandler(); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsProviderTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsProviderTest.java new file mode 100644 index 0000000000..919253837e --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/JavaMetricsProviderTest.java @@ -0,0 +1,77 @@ +/** + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.metrics; + + +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Map; + +import org.junit.Test; + +import net.sourceforge.pmd.lang.java.ParserTstUtil; +import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.MethodLikeNode; +import net.sourceforge.pmd.lang.java.metrics.api.JavaClassMetricKey; +import net.sourceforge.pmd.lang.java.metrics.api.JavaOperationMetricKey; +import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; +import net.sourceforge.pmd.lang.metrics.MetricKey; + + +/** + * @author Clément Fournier + */ +public class JavaMetricsProviderTest { + + @Test + public void testComputeAllMetrics() { + + LanguageMetricsProvider provider = ParserTstUtil.getLanguageVersionHandler("1.8").getLanguageMetricsProvider(); + + ASTCompilationUnit acu = ParserTstUtil.parseAndTypeResolveJava("1.8", + "class Foo { void bar() { System.out.println(1); } }"); + + ASTAnyTypeDeclaration type = acu.getFirstDescendantOfType(ASTAnyTypeDeclaration.class); + + Map, Double> results = provider.computeAllMetricsFor(type); + + for (JavaClassMetricKey key : JavaClassMetricKey.values()) { + assertTrue(results.containsKey(key)); + } + + MethodLikeNode op = acu.getFirstDescendantOfType(MethodLikeNode.class); + + Map, Double> opResults = provider.computeAllMetricsFor(op); + + for (JavaOperationMetricKey key : JavaOperationMetricKey.values()) { + assertTrue(opResults.containsKey(key)); + } + } + + + @Test + public void testThereIsNoMemoisation() { + + LanguageMetricsProvider provider = ParserTstUtil.getLanguageVersionHandler("1.8").getLanguageMetricsProvider(); + + ASTAnyTypeDeclaration tdecl1 = ParserTstUtil.parseAndTypeResolveJava("1.8", + "class Foo { void bar() { System.out.println(1); } }").getFirstDescendantOfType(ASTAnyTypeDeclaration.class); + + Map, Double> reference = provider.computeAllMetricsFor(tdecl1); + + ASTAnyTypeDeclaration tdecl2 = ParserTstUtil.parseAndTypeResolveJava("1.8", + // same name, different characteristics + "class Foo { void bar(){} \npublic void hey() { System.out.println(1); } }").getFirstDescendantOfType(ASTAnyTypeDeclaration.class); + + Map, Double> secondTest = provider.computeAllMetricsFor(tdecl2); + + assertNotEquals(reference, secondTest); + + } + + +} diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/ProjectMemoizerTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/ProjectMemoizerTest.java index eee8a7152b..b40fa44b5f 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/ProjectMemoizerTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/metrics/ProjectMemoizerTest.java @@ -74,7 +74,7 @@ public class ProjectMemoizerTest { @Override public Object visit(ASTMethodOrConstructorDeclaration node, Object data) { MetricMemoizer op = toplevel.getOperationMemoizer(node.getQualifiedName()); - result.add((int) JavaMetricsComputer.INSTANCE.computeForOperation(opMetricKey, node, force, + result.add((int) JavaMetricsComputer.getInstance().computeForOperation(opMetricKey, node, force, MetricOptions.emptyOptions(), op)); return super.visit(node, data); } @@ -83,7 +83,7 @@ public class ProjectMemoizerTest { @Override public Object visit(ASTAnyTypeDeclaration node, Object data) { MetricMemoizer clazz = toplevel.getClassMemoizer(node.getQualifiedName()); - result.add((int) JavaMetricsComputer.INSTANCE.computeForType(classMetricKey, node, force, + result.add((int) JavaMetricsComputer.getInstance().computeForType(classMetricKey, node, force, MetricOptions.emptyOptions(), clazz)); return super.visit(node, data); } diff --git a/pmd-java8/pom.xml b/pmd-java8/pom.xml index 9410e806dd..848229de5f 100644 --- a/pmd-java8/pom.xml +++ b/pmd-java8/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-javascript/pom.xml b/pmd-javascript/pom.xml index 92a22aa705..2633bde55f 100644 --- a/pmd-javascript/pom.xml +++ b/pmd-javascript/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-jsp/pom.xml b/pmd-jsp/pom.xml index bd00d84195..37c71999fb 100644 --- a/pmd-jsp/pom.xml +++ b/pmd-jsp/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-kotlin/pom.xml b/pmd-kotlin/pom.xml index 99d0e87ec9..3802679aae 100644 --- a/pmd-kotlin/pom.xml +++ b/pmd-kotlin/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-lang-test/pom.xml b/pmd-lang-test/pom.xml index a49d41ca1d..20f376e83b 100644 --- a/pmd-lang-test/pom.xml +++ b/pmd-lang-test/pom.xml @@ -12,7 +12,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-matlab/pom.xml b/pmd-matlab/pom.xml index a112ae29a2..f286ba6516 100644 --- a/pmd-matlab/pom.xml +++ b/pmd-matlab/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-objectivec/pom.xml b/pmd-objectivec/pom.xml index 441a91a398..0cca0c1c0f 100644 --- a/pmd-objectivec/pom.xml +++ b/pmd-objectivec/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-perl/pom.xml b/pmd-perl/pom.xml index 85ba19d877..6a95565e49 100644 --- a/pmd-perl/pom.xml +++ b/pmd-perl/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-php/pom.xml b/pmd-php/pom.xml index d9fa196b39..26870c3f4d 100644 --- a/pmd-php/pom.xml +++ b/pmd-php/pom.xml @@ -7,7 +7,7 @@ net.sourceforge.pmd pmd - 6.11.0-SNAPSHOT + 6.12.0-SNAPSHOT diff --git a/pmd-plsql/etc/grammar/PldocAST.jjt b/pmd-plsql/etc/grammar/PldocAST.jjt index 066a9eab8a..f08f27c0cc 100644 --- a/pmd-plsql/etc/grammar/PldocAST.jjt +++ b/pmd-plsql/etc/grammar/PldocAST.jjt @@ -220,9 +220,10 @@ ASTInput Input() : {} | LOOKAHEAD(6) Global() | LOOKAHEAD(6) DDLCommand() //Ignore any other DDL Event | LOOKAHEAD(2) SqlPlusCommand() - | LOOKAHEAD(2) UpdateStatement() - | LOOKAHEAD(2) DeleteStatement() - |(||||||) SkipPastNextTokenOccurrence(SQLPLUS_TERMINATOR) //Ignore SQL statements in scripts ) ("/")* )* @@ -1414,16 +1415,38 @@ ASTSqlExpression SqlExpression() : | LOOKAHEAD(2) | - LOOKAHEAD(2) AdditiveExpression() // this can be a literal or a simple expression, but no conditional + AdditiveExpression() // this can be a literal or a simple expression, but no conditional ) { return jjtThis; } } +/** + * See also https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/About-User-Defined-Functions.html#GUID-4EB3E236-8216-471C-BA44-23D87BDFEA67 + * + * A function reference might be: + * function_name + * package.function_name + * package.schema.function_name + * optional: @ dblink . + */ ASTFunctionCall FunctionCall() : -{ ASTID id; } { - id = ID() { jjtThis.setImage(id.getImage()); } Arguments() - { return jjtThis; } + ASTID id; + StringBuilder name = new StringBuilder(); +} +{ + id = ID() { name.append(id.getImage()); } + [ "." id = ID() { name.append('.').append(id.getImage()); } + [ "." id = ID() { name.append('.').append(id.getImage()); } ] + ] + [ "@" id = ID() { name.append('@').append(id.getImage()); } ] + + Arguments() + + { + jjtThis.setImage(name.toString()); + return jjtThis; + } } ASTColumn Column() : @@ -1542,18 +1565,41 @@ ASTHostArrayName HostArrayName() : ASTTableReference TableReference() : {} { - // QueryTableExpression - ( - [ LOOKAHEAD(2) SchemaName() "." ] TableName() - | - "(" Subquery() ")" - ) + QueryTableExpression() [ LOOKAHEAD(2) TableAlias() ] { return jjtThis; } } +void QueryTableExpression() #void : +{} +{ + ( + LOOKAHEAD(2) [ LOOKAHEAD(2) SchemaName() "." ] TableName() + | + TableCollectionExpression() + | + [ ] "(" Subquery() [ SubqueryRestrictionClause() ] ")" + ) +} + +ASTSubqueryRestrictionClause SubqueryRestrictionClause() : +{} +{ + ( |
"(" (LOOKAHEAD(3) Subquery() | Expression()) ")" [ "(" "+" ")" ] + + { return jjtThis; } +} + /** * Special production, used in joins. The table reference might have * a table alias, but this should not match any following NATURAL, CROSS, etc. @@ -1563,12 +1609,7 @@ ASTTableReference TableReference() : ASTTableReference TableReferenceInJoin() #TableReference : {} { - // QueryTableExpression - ( - [ LOOKAHEAD(2) SchemaName() "." ] TableName() - | - "(" Subquery() ")" - ) + QueryTableExpression() [ LOOKAHEAD(1, ID(), {getToken(1).kind != NATURAL && getToken(1).kind != CROSS @@ -1762,6 +1803,7 @@ ASTUnlabelledStatement UnlabelledStatement() : SelectIntoStatement() ";" | UpdateStatement() ";" | DeleteStatement() ";" | + InsertStatement() ";" | LOOKAHEAD(["("]
||) SqlStatement(null,";") [";"] | LOOKAHEAD(3) ContinueStatement() ";" // CONTINUE keyword was added in 11G, so Oracle compilation supports CONTINUE as a variable name | CaseStatement() ";" @@ -1871,6 +1913,55 @@ ASTCursorForLoopStatement CursorForLoopStatement() : { return jjtThis ; } } +/** + * See https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/INSERT.html#GUID-903F8043-0254-4EE9-ACC1-CB8AC0AF3423 + */ +ASTInsertStatement InsertStatement() : +{} +{ + ( SingleTableInsert() | MultiTableInsert() ) + { return jjtThis; } +} + +ASTSingleTableInsert SingleTableInsert() : +{} +{ + InsertIntoClause() ( ValuesClause() | Subquery() ) + { return jjtThis; } +} + +ASTInsertIntoClause InsertIntoClause() : +{} +{ + DMLTableExpressionClause() [ LOOKAHEAD(2) TableAlias() ] [ LOOKAHEAD(2) "(" Column() ( "," Column() )* ")" ] + { return jjtThis; } +} + +ASTValuesClause ValuesClause() : +{} +{ + "(" ( Expression() | <_DEFAULT> ) ( "," ( Expression() | <_DEFAULT> ) )* ")" + { return jjtThis; } +} +ASTMultiTableInsert MultiTableInsert() : +{} +{ + ( + LOOKAHEAD(2) ( InsertIntoClause() [ ValuesClause() ] )+ Subquery() + | + ConditionalInsertClause() + ) + { return jjtThis; } +} + +ASTConditionalInsertClause ConditionalInsertClause() : +{} +{ + [ | ] ( Condition() ( InsertIntoClause() [ ValuesClause() ] )+ )+ + [ ( InsertIntoClause() [ ValuesClause() ] )+ ] + { return jjtThis; } +} + ASTSelectStatement SelectStatement() : {} { @@ -1892,21 +1983,35 @@ ASTSelectStatement SelectStatement() : ASTUpdateStatement UpdateStatement() : {} { - SqlExpression() + DMLTableExpressionClause() [ LOOKAHEAD(2) TableAlias() ] UpdateSetClause() [ WhereClause() ] { return jjtThis; } } +ASTDMLTableExpressionClause DMLTableExpressionClause() : +{} +{ + ( + TableCollectionExpression() + | + LOOKAHEAD(2) [ LOOKAHEAD(2) SchemaName() "." ] TableName() + | + [ ] "(" Subquery() [ SubqueryRestrictionClause() ] ")" + ) + + { return jjtThis; } +} + ASTUpdateSetClause UpdateSetClause() : {} { ( - "=" ID() + LOOKAHEAD(3) "=" ID() | - Column() "=" ( Expression() | <_DEFAULT> ) - ( "," Column() "=" ( Expression() | <_DEFAULT> )*)* + [ LOOKAHEAD(2) TableName() "." ] Column() "=" ( Expression() | <_DEFAULT> ) + ( "," [ LOOKAHEAD(2) TableName() "." ] Column() "=" ( Expression() | <_DEFAULT> ) )* ) { return jjtThis; } } @@ -4405,6 +4510,9 @@ TOKEN [IGNORE_CASE]: | | | + +| +| } /** @@ -4991,7 +5099,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| | | -//| +| //| //| //| @@ -5342,7 +5450,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -//| +| //| | //| @@ -5447,7 +5555,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| | //| -| //SET is defined as a reserved word but is used in "SYS"."DBMS_RESULT_CACHE_API" as a function name and as a Pragma parameter +//| //SET is defined as a reserved word but is used in "SYS"."DBMS_RESULT_CACHE_API" as a function name and as a Pragma parameter //| | //| @@ -5833,7 +5941,7 @@ ASTID ID(): {} | | //RESERVED WORD | - |
//RESERVED WORD + //|
//RESERVED WORD | //SYNTAX //RESERVED WORD // |