Merge branch 'master' into issue-4176-NonSerializableClass

This commit is contained in:
Andreas Dangel committed 2022-11-17 16:29:25 +01:00
commit aed682453b
42 files changed
+1006 -309

No files matched your search

+18
View File
@@ -6988,6 +6988,24 @@
"contributions": [
"doc"
]
},
{
"login": "jvwilge",
"name": "Jeroen van Wilgenburg",
"avatar_url": "https://avatars.githubusercontent.com/u/251901?v=4",
"profile": "https://vanwilgenburg.wordpress.com/",
"contributions": [
"doc"
]
},
{
"login": "Eldrick19",
"name": "Eldrick Wega",
"avatar_url": "https://avatars.githubusercontent.com/u/26189114?v=4",
"profile": "https://github.com/Eldrick19",
"contributions": [
"doc"
]
}
],
"contributorsPerLine": 7,
+2 -1
View File
@@ -15,7 +15,8 @@ It uses JavaCC and Antlr to parse source files into abstract syntax trees (AST)
Rules can be written in Java or using a XPath query.
It supports Java, JavaScript, Salesforce.com Apex and Visualforce,
Modelica, PLSQL, Apache Velocity, XML, XSL, Scala.
Modelica, PLSQL, Apache Velocity, XML, XSL.
Scala is supported, but there are currently no Scala rules available.
Additionally it includes **CPD**, the copy-paste-detector. CPD finds duplicated code in
C/C++, C#, Dart, Fortran, Go, Groovy, Java, JavaScript, JSP, Kotlin, Lua, Matlab, Modelica,
File diff suppressed because it is too large. Load diff
+16 -4
View File
@@ -87,6 +87,8 @@ The tool comes with a rather extensive help text, simply running with `--help`!
the given language `<lang>`. Parsing errors are ignored and unparsable files
are skipped.
<p>Use `--use-version` to specify the language version to use, if it is not the default.</p>
<p>This option allows to use the xml language for files, that don't
use xml as extension. See [example](#analyze-other-xml-formats) below.</p>"
%}
@@ -99,6 +101,16 @@ The tool comes with a rather extensive help text, simply running with `--help`!
{% include custom/cli_option_row.html options="--help,-h,-H"
description="Display help on usage."
%}
{% include custom/cli_option_row.html options="--use-version"
option_arg="lang-version"
description="The specific language version PMD should use when parsing source code for a given language.
<p>Values are in the format of *language-version*.</p>
<p>This option can be repeated to configure several languages for the same run.</p>
<p>Note that this option does not change how languages are assigned to files.
It only changes something if the project you analyze contains some files that PMD detects as the given language.
Language detection is only influenced by file extensions and the `--force-language` option.</p>
<p>See also [Supported Languages](#supported-languages).</p>"
%}
{% include custom/cli_option_row.html options="-language,-l"
option_arg="lang"
description="Specify the language PMD should use. Used together with `-version`. See also [Supported Languages](#supported-languages)."
@@ -184,16 +196,16 @@ This behavior has been introduced to ease PMD integration into scripts or hooks,
The language is determined automatically by PMD from the file extensions. Some languages such as "Java"
however support multiple versions. The default version will be used, which is usually the latest supported
version. If you want to use an older version, so that e.g. rules, that suggest usage of language features,
that are not available yet, won't be executed, you need to specify a specific version via the `-language`
and `-version` parameter.
non-preview version. If you want to use an older version, so that e.g. rules that suggest usage of language features
that are not available yet won't be executed, you need to specify a specific version via the `--use-version`
parameter.
These parameters are irrelevant for languages that don't support different versions.
Example:
``` shell
./run.sh pmd -d src/main/java -f text -R rulesets/java/quickstart.xml -language java -version 8
./run.sh pmd -d src/main/java -f text -R rulesets/java/quickstart.xml --use-version java-1.8
```
* [apex](pmd_rules_apex.html) (Salesforce Apex)
+6 -4
View File
@@ -62,7 +62,7 @@ Novice as much as advanced readers may want to [read on on Refactoring Guru](htt
description="The minimum token length which should be reported as a duplicate."
required="yes"
%}
{% include custom/cli_option_row.html options="--files"
{% include custom/cli_option_row.html options="--files,--dir,-d"
description="List of files and directories to process"
required="yes"
%}
@@ -73,10 +73,10 @@ Novice as much as advanced readers may want to [read on on Refactoring Guru](htt
description="Sources code language."
default="java"
%}
{% include custom/cli_option_row.html options="--debug,--verbose"
{% include custom/cli_option_row.html options="--debug,--verbose,-v,-D"
description="Debug mode. Prints more log output."
%}
{% include custom/cli_option_row.html options="--encoding"
{% include custom/cli_option_row.html options="--encoding,-e"
description="Character encoding to use when processing files. If not specified, CPD uses the system default encoding."
%}
{% include custom/cli_option_row.html options="--skip-duplicate-files"
@@ -220,8 +220,10 @@ This behavior has been introduced to ease CPD integration into scripts or hooks,
* Dart
* EcmaScript (JavaScript)
* Fortran
* Gherkin (Cucumber)
* Go
* Groovy
* Html
* Java
* Jsp
* Kotlin
@@ -368,7 +370,7 @@ For Windows:
For Linux:
./run.sh cpdgui
./run.sh cpd-gui
Here's a screenshot of CPD after running on the JDK 8 java.lang package:
+1 -1
View File
@@ -23,7 +23,7 @@ sidebar: pmd_sidebar
* For Windows: [Winzip](http://winzip.com) or the free [7-zip](http://www.7-zip.org/)
* For Linux / Unix: [InfoZip](http://infozip.sourceforge.net/)
{% include note.html content="For executing the Designer (./run.sh designer) using [OpenJDK](http://jdk.java.net) or Java 11, you need additionally [OpenJFX](https://openjfx.io/). Download it, extract it and set the environment variable JAVAFX_HOME." %}
{% include note.html content="For executing the Designer (./run.sh designer) using [OpenJDK](http://jdk.java.net) or Java 11, you need additionally [JavaFX](https://gluonhq.com/products/javafx/). Download it, extract it and set the environment variable JAVAFX_HOME pointing at that directory." %}
### Installation
+65
View File
@@ -23,15 +23,80 @@ This is a {{ site.pmd.release_type }} release.
The property `prefix` has been deprecated, since in a serializable class all fields have to be
serializable regardless of the name.
#### Modified rules
* The rule {% rule java/codestyle/ClassNamingConventions %} has a new property `testClassPattern`, which is applied
to test classes. By default, test classes should end with the suffix "Test". Test classes are top-level classes, that
either inherit from JUnit 3 TestCase or have at least one method annotated with the Test annotations from
JUnit4/5 or TestNG.
### Fixed Issues
* cli
* [#4215](https://github.com/pmd/pmd/discussions/4215): NullPointerException when trying to open designer
* java
* [#3643](https://github.com/pmd/pmd/issues/3643): \[java] More parser edge cases
* [#4152](https://github.com/pmd/pmd/issues/4152): \[java] Parse error on array type annotations
* java-codestyle
* [#2867](https://github.com/pmd/pmd/issues/2867): \[java] Separate pattern for test classes in ClassNamingConventions rule for Java
* [#4201](https://github.com/pmd/pmd/issues/4201): \[java] CommentDefaultAccessModifier should consider lombok's @<!-- -->Value
* java-design
* [#4200](https://github.com/pmd/pmd/issues/4200): \[java] ClassWithOnlyPrivateConstructorsShouldBeFinal should consider lombok's @<!-- -->Value
* java-errorprone
* [#1668](https://github.com/pmd/pmd/issues/1668): \[java] BeanMembersShouldSerialize is extremely noisy
* [#4176](https://github.com/pmd/pmd/issues/4176): \[java] Rename BeanMembersShouldSerialize to NonSerializableClass
* [#4185](https://github.com/pmd/pmd/issues/4185): \[java] InvalidLogMessageFormat rule produces a NPE
### API Changes
#### PMD CLI
* PMD now supports a new `--use-version` flag, which receives a language-version pair (such as `java-8` or `apex-54`).
This supersedes the usage of `-language` / `-l` and `-version` / `-v`, allowing for multiple versions to be set in a single run.
PMD 7 will completely remove support for `-language` and `-version` in favor of this new flag.
* Support for `-V` is being deprecated in favor of `--verbose` in preparation for PMD 7.
In PMD 7, `-v` will enable verbose mode and `-V` will show the PMD version for consistency with most Unix/Linux tools.
* Support for `-min` is being deprecated in favor of `--minimum-priority` for consistency with most Unix/Linux tools, where `-min` would be equivalent to `-m -i -n`.
#### CPD CLI
* CPD now supports using `-d` or `--dir` as an alias to `--files`, in favor of consistency with PMD.
PMD 7 will remove support for `--files` in favor of these new flags.
#### Linux run.sh parameters
* Using `run.sh cpdgui` will now warn about it being deprecated. Use `run.sh cpd-gui` instead.
* The old designer (`run.sh designerold`) is completely deprecated and will be removed in PMD 7. Switch to the new JavaFX designer: `run.sh designer`.
* The old visual AST viewer (`run.sh bgastviewer`) is completely deprecated and will be removed in PMD 7. Switch to the new JavaFX designer: `run.sh designer` for a visual tool, or use `run.sh ast-dump` for a text-based aleternative.
#### Deprecated API
The following APIs have been marked as deprecated for removal in PMD 7:
- {% jdoc core::PMD %} and {% jdoc core::PMD.StatusCode %} - PMD 7 will ship with a revamped CLI split from pmd-core. To programatically launch analysis you can use {% jdoc core::PmdAnalysis %}.
- {% jdoc !!core::PMDConfiguration#getAllInputPaths() %} - It is now superceded by {% jdoc !!core::PMDConfiguration#getInputPathList() %}
- {% jdoc !!core::PMDConfiguration#setInputPaths(List) %} - It is now superceded by {% jdoc !!core::PMDConfiguration#setInputPathList(List) %}
- {% jdoc !!core::PMDConfiguration#addInputPath(String) %} - It is now superceded by {% jdoc !!core::PMDConfiguration#addInputPath(Path) %}
- {% jdoc !!core::PMDConfiguration#getInputFilePath() %} - It is now superceded by {% jdoc !!core::PMDConfiguration#getInputFile() %}
- {% jdoc !!core::PMDConfiguration#getIgnoreFilePath() %} - It is now superceded by {% jdoc !!core::PMDConfiguration#getIgnoreFile() %}
- {% jdoc !!core::PMDConfiguration#setInputFilePath(String) %} - It is now superceded by {% jdoc !!core::PMDConfiguration#setInputFilePath(Path) %}
- {% jdoc !!core::PMDConfiguration#setIgnoreFilePath(String) %} - It is now superceded by {% jdoc !!core::PMDConfiguration#setIgnoreFilePath(Path) %}
- {% jdoc !!core::PMDConfiguration#getInputUri() %} - It is now superceded by {% jdoc !!core::PMDConfiguration#getUri() %}
- {% jdoc !!core::PMDConfiguration#setInputUri(String) %} - It is now superceded by {% jdoc !!core::PMDConfiguration#setInputUri(URI) %}
- {% jdoc !!core::PMDConfiguration#getReportFile() %} - It is now superceded by {% jdoc !!core::PMDConfiguration#getReportFilePath() %}
- {% jdoc !!core::PMDConfiguration#setReportFile(String) %} - It is now superceded by {% jdoc !!core::PMDConfiguration#setReportFile(Path) %}
- {% jdoc !!core::PMDConfiguration#isStressTest() %} and {% jdoc !!core::PMDConfiguration#setStressTest(boolean) %} - Will be removed with no replacement.
- {% jdoc !!core::PMDConfiguration#isBenchmark() %} and {% jdoc !!core::PMDConfiguration#setBenchmark(boolean) %} - Will be removed with no replacement, the CLI will still support it.
- {% jdoc core::cpd.CPD %} and {% jdoc core::cpd.CPD.StatusCode %} - PMD 7 will ship with a revamped CLI split from pmd-core. An alterative to programatically launch CPD analysis will be added in due time.
### External Contributions
* [#4184](https://github.com/pmd/pmd/pull/4184): \[java]\[doc] TestClassWithoutTestCases - fix small typo in description - [Valery Yatsynovich](https://github.com/valfirst) (@valfirst)
* [#4198](https://github.com/pmd/pmd/pull/4198): \[doc] Add supported CPD languages - [Jeroen van Wilgenburg](https://github.com/jvwilge) (@jvwilge)
* [#4202](https://github.com/pmd/pmd/pull/4202): \[java] Fix #4200 and #4201: ClassWithOnlyPrivateConstructorsShouldBeFinal, CommentDefaultAccessModifier: Exclude lombok @<!-- -->Value annotation - [Lynn](https://github.com/LynnBroe) (@LynnBroe)
* [#4205](https://github.com/pmd/pmd/pull/4205): \[doc] Clarify Scala support (no built-in rules) - [Eldrick Wega](https://github.com/Eldrick19) (@Eldrick19)
{% endtocmaker %}
@@ -60,7 +60,10 @@ import net.sourceforge.pmd.util.log.internal.SimpleMessageReporter;
*
* <p><strong>Warning:</strong> This class is not intended to be instantiated or subclassed. It will
* be made final in PMD7.
*
* @deprecated This class is to be removed in PMD 7 in favor of a unified PmdCli entry point. {@link PmdAnalysis} should be used for non-CLI use-cases.
*/
@Deprecated
public class PMD {
@@ -539,7 +542,9 @@ public class PMD {
* Represents status codes that are used as exit codes during CLI runs.
*
* @see #runPmd(String[])
* @deprecated This class is to be removed in PMD 7 in favor of a unified PmdCli entry point.
*/
@Deprecated
public enum StatusCode {
/** No errors, no violations. This is exit code {@code 0}. */
OK(0),
File diff suppressed because it is too large. Load diff
@@ -14,6 +14,7 @@ import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDConfiguration;
import net.sourceforge.pmd.RulePriority;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageRegistry;
import net.sourceforge.pmd.lang.LanguageVersion;
@@ -163,6 +164,9 @@ public class PMDParameters {
@Parameter(names = { "--no-cache", "-no-cache" }, description = "Explicitly disable incremental analysis. The '-cache' option is ignored if this switch is present in the command line.")
private boolean noCache = false;
@Parameter(names = "--use-version", description = "The language version PMD should use when parsing source code in the language-version format, ie: 'java-1.8'")
private List<String> languageVersions = new ArrayList<>();
// this has to be a public static class, so that JCommander can use it!
public static class PropertyConverter implements IStringConverter<Properties> {
@@ -258,6 +262,24 @@ public class PMDParameters {
configuration.getLanguageVersionDiscoverer().setDefaultLanguageVersion(languageVersion);
}
for (String langVerStr : this.getLanguageVersions()) {
int dashPos = langVerStr.indexOf('-');
if (dashPos == -1) {
throw new IllegalArgumentException("Invalid language version: " + langVerStr);
}
String langStr = langVerStr.substring(0, dashPos);
String verStr = langVerStr.substring(dashPos + 1);
Language lang = LanguageRegistry.findLanguageByTerseName(langStr);
LanguageVersion langVer = null;
if (lang != null) {
langVer = lang.getVersion(verStr);
}
if (lang == null || langVer == null) {
throw new IllegalArgumentException("Invalid language version: " + langVerStr);
}
configuration.getLanguageVersionDiscoverer().setDefaultLanguageVersion(langVer);
}
try {
configuration.prependAuxClasspath(this.getAuxclasspath());
} catch (IllegalArgumentException e) {
@@ -348,6 +370,10 @@ public class PMDParameters {
return language != null ? language : LanguageRegistry.getDefaultLanguage().getTerseName();
}
public List<String> getLanguageVersions() {
return languageVersions;
}
public String getForceLanguage() {
return forceLanguage != null ? forceLanguage : "";
}
@@ -166,6 +166,13 @@ public final class PmdParametersParseResult {
m.put("-norulesetcompatibility", "--no-ruleset-compatibility");
m.put("-cache", "--cache");
m.put("-no-cache", "--no-cache");
m.put("-v", "--use-version"); // In PMD 7, -v will enable verbose mode
m.put("-V", "--verbose"); // In PMD 7, -V will show the tool version
m.put("-min", "--minimum-priority");
m.put("-version", "--use-version");
m.put("-language", "--use-version");
m.put("-l", "--use-version");
SUGGESTED_REPLACEMENT = Collections.unmodifiableMap(m);
}
}
@@ -31,6 +31,10 @@ import net.sourceforge.pmd.util.database.DBURI;
import net.sourceforge.pmd.util.database.SourceObject;
import net.sourceforge.pmd.util.log.ScopedLogHandlersManager;
/**
* @deprecated This class is to be removed in PMD 7 in favor of a unified PmdCli entry point.
*/
@Deprecated
public class CPD {
private static final Logger LOGGER = Logger.getLogger(CPD.class.getName());
@@ -257,6 +261,10 @@ public class CPD {
return new CPDReport(matchAlgorithm.getMatches(), numberOfTokensPerFile);
}
/**
* @deprecated This class is to be removed in PMD 7 in favor of a unified PmdCli entry point.
*/
@Deprecated
public enum StatusCode {
OK(0),
ERROR(1),
@@ -133,6 +133,7 @@ public final class CPDCommandLineInterface {
m.put("--failOnViolation", "--fail-on-violation");
m.put("-failOnViolation", "--fail-on-violation");
m.put("--filelist", "--file-list");
m.put("--files", "--dir");
SUGGESTED_REPLACEMENT = Collections.unmodifiableMap(m);
}
@@ -116,7 +116,7 @@ public class CPDConfiguration extends AbstractConfiguration {
required = false)
private String skipBlocksPattern = Tokenizer.DEFAULT_SKIP_BLOCKS_PATTERN;
@Parameter(names = "--files", variableArity = true, description = "List of files and directories to process",
@Parameter(names = { "--files", "-d", "--dir" }, variableArity = true, description = "List of files and directories to process",
required = false, converter = FileConverter.class)
private List<File> files;
@@ -141,7 +141,7 @@ public class CPDConfiguration extends AbstractConfiguration {
description = "By default CPD exits with status 4 if code duplications are found. Disable this option with '-failOnViolation false' to exit with 0 instead and just write the report.")
private boolean failOnViolation = true;
@Parameter(names = { "--debug", "--verbose" }, description = "Debug mode.")
@Parameter(names = { "--debug", "--verbose", "-v", "-D" }, description = "Debug mode.")
private boolean debug = false;
// this has to be a public static class, so that JCommander can use it!
@@ -156,7 +156,7 @@ public class CPDConfiguration extends AbstractConfiguration {
}
}
@Parameter(names = "--encoding", description = "Character encoding to use when processing files", required = false)
@Parameter(names = { "--encoding", "-e" }, description = "Character encoding to use when processing files", required = false)
public void setEncoding(String encoding) {
this.encoding = encoding;
setSourceEncoding(encoding);
+11 -3
View File
@@ -10,7 +10,7 @@ usage() {
}
valid_app_options () {
echo "pmd, cpd, cpdgui, designer, bgastviewer, designerold, ast-dump"
echo "pmd, cpd, cpd-gui, designer, bgastviewer, designerold, ast-dump"
}
is_cygwin() {
@@ -159,10 +159,12 @@ function add_openjfx_classpath() {
then
script_exit "The environment variable JAVAFX_HOME is missing."
else
# The wildcard will include only jar files, but we need to access also
# property files such as javafx.properties that lay bare in the dir
if [ -n "$classpath" ]; then
classpath="$classpath:${JAVAFX_HOME}/lib/*"
classpath="$classpath:${JAVAFX_HOME}/lib/*:${JAVAFX_HOME}/lib/"
else
classpath="${JAVAFX_HOME}/lib/*"
classpath="${JAVAFX_HOME}/lib/*:${JAVAFX_HOME}/lib/"
fi
fi
fi
@@ -187,12 +189,18 @@ case "${APPNAME}" in
readonly CLASSNAME="net.sourceforge.pmd.util.fxdesigner.DesignerStarter"
;;
"designerold")
echo "'designerold' is deprecated and will be removed in PMD 7.0.0, try the new 'designer' instead."
readonly CLASSNAME="net.sourceforge.pmd.util.designer.Designer"
;;
"bgastviewer")
echo "'bgastviewer' is deprecated and will be removed in PMD 7.0.0, try the new 'designer' instead."
readonly CLASSNAME="net.sourceforge.pmd.util.viewer.Viewer"
;;
"cpdgui")
echo "'cpdgui' is deprecated and will be removed in PMD 7.0.0, use 'cpd-gui' instead."
readonly CLASSNAME="net.sourceforge.pmd.cpd.GUI"
;;
"cpd-gui")
readonly CLASSNAME="net.sourceforge.pmd.cpd.GUI"
;;
"ast-dump")
+54 -42
View File
@@ -736,6 +736,12 @@ public class JavaParser {
token_source.setSuppressMarker(marker);
}
/**
* Keeps track during tree construction, whether we are currently building an
* explicit constructor invocation. Then the PrimaryExpression that may prefix
* a qualified super constructor call may not consume "super" tokens.
*/
private boolean inExplicitConstructorInvoc = false;
}
PARSER_END(JavaParser)
@@ -1377,7 +1383,7 @@ void RecordComponent():
{
(RecordComponentModifier())*
Type()
[ "..." {jjtThis.setVarargs();} ]
[ (TypeAnnotation())* "..." {jjtThis.setVarargs();} ]
VariableDeclaratorId()
}
@@ -1454,7 +1460,7 @@ void ClassOrInterfaceBodyDeclaration():
| LOOKAHEAD({isKeyword("enum")}) EnumDeclaration(modifiers)
| LOOKAHEAD({isKeyword("record")}) RecordDeclaration(modifiers)
| LOOKAHEAD( [ TypeParameters() ] <IDENTIFIER> "(" ) ConstructorDeclaration(modifiers)
| LOOKAHEAD( Type() <IDENTIFIER> ( "[" "]" )* ( "," | "=" | ";" ) ) FieldDeclaration(modifiers)
| LOOKAHEAD( Type() <IDENTIFIER> ( (Annotation())* "[" "]" )* ( "," | "=" | ";" ) ) FieldDeclaration(modifiers)
| LOOKAHEAD(2) MethodDeclaration(modifiers)
| LOOKAHEAD(2) AnnotationTypeDeclaration(modifiers)
)
@@ -1483,7 +1489,7 @@ void VariableDeclaratorId() :
{
(LOOKAHEAD(2) t=<IDENTIFIER> "." <THIS> { checkforBadExplicitReceiverParameter(); jjtThis.setExplicitReceiverParameter(); image=t.image + ".this"; }
| t=<THIS> { checkforBadExplicitReceiverParameter(); jjtThis.setExplicitReceiverParameter(); image = t.image;}
| t=<IDENTIFIER> { image = t.image; } ( "[" "]" { jjtThis.bumpArrayDepth(); })*
| t=<IDENTIFIER> { image = t.image; } ( (TypeAnnotation())* "[" "]" { jjtThis.bumpArrayDepth(); })*
)
{
checkForBadAssertUsage(image, "a variable name");
@@ -1526,7 +1532,7 @@ void MethodDeclarator() :
checkForBadEnumUsage(t.image, "a method name");
jjtThis.setImage( t.image );
}
FormalParameters() ( "[" "]" )*
FormalParameters() ( (TypeAnnotation())* "[" "]" )*
}
@@ -1541,8 +1547,8 @@ void FormalParameter() :
}
{
( "final" {jjtThis.setFinal(true);} | Annotation() )*
Type() ("|" {checkForBadMultipleExceptionsCatching();} Type())*
[ "..." {checkForBadVariableArgumentsUsage();} {jjtThis.setVarargs();} ]
Type() ("|" {checkForBadMultipleExceptionsCatching();} (TypeAnnotation())* Type())*
[ (TypeAnnotation())* "..." {checkForBadVariableArgumentsUsage();} {jjtThis.setVarargs();} ]
VariableDeclaratorId()
}
@@ -1559,13 +1565,14 @@ Token t;}
}
void ExplicitConstructorInvocation() :
{}
{
LOOKAHEAD("this" Arguments() ";") "this" {jjtThis.setIsThis();} Arguments() ";"
{boolean prev = inExplicitConstructorInvoc; inExplicitConstructorInvoc = true;}
{ (
LOOKAHEAD([TypeArguments()] "this" Arguments() ";") [TypeArguments()] "this" {jjtThis.setIsThis();}
|
LOOKAHEAD(TypeArguments() "this" Arguments() ";") TypeArguments() "this" {jjtThis.setIsThis();} Arguments() ";"
|
[ LOOKAHEAD(PrimaryExpression() ".") PrimaryExpression() "." ] [ TypeArguments() ] "super" {jjtThis.setIsSuper();} Arguments() ";"
[ LOOKAHEAD(PrimaryExpression() "." [TypeArguments()] "super" ) PrimaryExpression() "." ] [ TypeArguments() ] "super" {jjtThis.setIsSuper();}
)
{inExplicitConstructorInvoc = prev;}
Arguments() ";"
}
void Initializer() :
@@ -1586,18 +1593,16 @@ void Type():
Token t;
}
{
LOOKAHEAD(2) ReferenceType()
LOOKAHEAD(<IDENTIFIER> | PrimitiveType() (TypeAnnotation())* "[" "]" ) ReferenceType()
| PrimitiveType()
}
void ReferenceType():
{}
{
// The grammar here is mildly wrong, the annotations can be before each []
// This will wait for #997
PrimitiveType() (TypeAnnotation())* ( LOOKAHEAD(2) "[" "]" { jjtThis.bumpArrayDepth(); })+
PrimitiveType() (LOOKAHEAD((TypeAnnotation())* "[" "]") (TypeAnnotation())* "[" "]" { jjtThis.bumpArrayDepth(); })+
|
( ClassOrInterfaceType()) (TypeAnnotation())* ( LOOKAHEAD(2) "[" "]" { jjtThis.bumpArrayDepth(); })*
ClassOrInterfaceType() (LOOKAHEAD((TypeAnnotation())* "[" "]") (TypeAnnotation())* "[" "]" { jjtThis.bumpArrayDepth(); })*
}
void ClassOrInterfaceType():
@@ -1608,7 +1613,7 @@ void ClassOrInterfaceType():
{
t=<IDENTIFIER> {s.append(t.image);}
[ LOOKAHEAD(2) TypeArguments() ]
( LOOKAHEAD(2) "." t=<IDENTIFIER> {s.append('.').append(t.image);} [ LOOKAHEAD(2) TypeArguments() ] )*
( LOOKAHEAD(2) "." (TypeAnnotation())* t=<IDENTIFIER> {s.append('.').append(t.image);} [ LOOKAHEAD(2) TypeArguments() ] )*
{jjtThis.setImage(s.toString());}
}
@@ -1740,8 +1745,6 @@ void AssignmentOperator() :
| "|=" {jjtThis.setImage("|="); jjtThis.setCompound();}
}
// TODO Setting isTernary is unnecessary, since the node is only pushed on the stack if there is at least one child,
// ie if it's a ternary
void ConditionalExpression() #ConditionalExpression(>1) :
{}
{
@@ -1787,7 +1790,7 @@ void EqualityExpression() #EqualityExpression(>1):
void Pattern() #void:
{}
{
LOOKAHEAD(ReferenceType() "(") RecordPattern()
LOOKAHEAD((Annotation())* ReferenceType() "(") RecordPattern()
| LOOKAHEAD("(") ParenthesizedPattern()
| TypePattern() [ GuardedPatternCondition() #GuardedPattern(2) {checkForGuardedPatterns();} ]
}
@@ -1815,7 +1818,7 @@ void TypePattern():
void RecordPattern():
{ checkForRecordPatterns(); }
{
ReferenceType() RecordStructurePattern() [ VariableDeclaratorId() ]
(Annotation())* ReferenceType() RecordStructurePattern() [ VariableDeclaratorId() ]
}
void RecordStructurePattern() #ComponentPatternList:
@@ -1836,14 +1839,13 @@ void InstanceOfExpression() #InstanceOfExpression(>1):
RelationalExpression()
[ "instanceof"
(
LOOKAHEAD("final" | "@") {checkforBadInstanceOfPattern();} Pattern()
|
LOOKAHEAD("(") Pattern() {checkForParenthesizedInstanceOfPattern();}
|
LOOKAHEAD(ReferenceType() "(") RecordPattern()
|
Type()
[ {checkforBadInstanceOfPattern();} VariableDeclaratorId() #TypePattern(2) ]
// Note: this can be simplified when support for java 18 preview is removed.
// Here the production Pattern is inlined to avoid that a following conditional &&
// be parsed as a pattern guard.
LOOKAHEAD("final" | (Annotation())* Type() <IDENTIFIER>) {checkforBadInstanceOfPattern();} TypePattern()
| LOOKAHEAD((Annotation())* ReferenceType() "(") {checkForRecordPatterns();} RecordPattern()
| LOOKAHEAD("(") {checkForParenthesizedInstanceOfPattern();} ParenthesizedPattern()
| (Annotation())* Type()
)
]
}
@@ -1959,9 +1961,18 @@ void SwitchExpression() :
void PrimaryExpression() :
{}
{
PrimaryPrefix() ( LOOKAHEAD(2) PrimarySuffix() )*
PrimaryPrefix() ( LOOKAHEAD(SuffixLAhead()) PrimarySuffix() )*
}
private void SuffixLAhead() #void:
{}
{
"::" | "[" | "("
| LOOKAHEAD({!inExplicitConstructorInvoc}) "."
| LOOKAHEAD({inExplicitConstructorInvoc}) "." (<IDENTIFIER> | TypeArguments() <IDENTIFIER> | "new") // not super or this in this case
}
void MemberSelector():
{
Token t;
@@ -1989,8 +2000,8 @@ void PrimaryPrefix() :
| LOOKAHEAD(3) "(" Expression() ")"
| AllocationExpression()
| LOOKAHEAD( ResultType() "." "class" ) ResultType() "." "class"
| LOOKAHEAD( Name() "::" ) Name()
| LOOKAHEAD( ReferenceType() MethodReference() ) ReferenceType() MethodReference()
| LOOKAHEAD( Name() "::" ) Name() // followed by method reference in PrimarySuffix
| LOOKAHEAD( "@" | Type() "::" ) (Annotation())* (LOOKAHEAD(2) ReferenceType()|PrimitiveType()) // followed by method reference in PrimarySuffix
| Name()
}
@@ -2019,7 +2030,7 @@ void LambdaParameter() #FormalParameter :
{
( "final" {jjtThis.setFinal(true);} | Annotation() )*
LambdaParameterType()
[ "..." {checkForBadVariableArgumentsUsage();} {jjtThis.setVarargs();} ]
[(TypeAnnotation())* "..." {checkForBadVariableArgumentsUsage();} {jjtThis.setVarargs();} ]
VariableDeclaratorId()
}
@@ -2079,10 +2090,12 @@ void ArgumentList() :
void AllocationExpression():
{}
{
"new" (TypeAnnotation())*
(LOOKAHEAD(2)
PrimitiveType() ArrayDimsAndInits()
"new"
(LOOKAHEAD((TypeAnnotation())* PrimitiveType())
(TypeAnnotation())* PrimitiveType() ArrayDimsAndInits()
|
[TypeArguments()]
(TypeAnnotation())*
ClassOrInterfaceType()
(
ArrayDimsAndInits()
@@ -2105,11 +2118,10 @@ void AllocationExpression():
void ArrayDimsAndInits() :
{}
{
LOOKAHEAD((TypeAnnotation())* "[" "]") ((TypeAnnotation())* "[" "]" {jjtThis.bumpArrayDepth();})+ ArrayInitializer()
| ( LOOKAHEAD((TypeAnnotation())* "[" UnaryExprNotPmStart() ) (TypeAnnotation())* "[" Expression() "]" {jjtThis.bumpArrayDepth();} )+
( LOOKAHEAD((TypeAnnotation())* "[") (TypeAnnotation())* "[" "]" {jjtThis.bumpArrayDepth();} )*
LOOKAHEAD(2)
( LOOKAHEAD(2) (TypeAnnotation())* "[" Expression() "]" {jjtThis.bumpArrayDepth();})+ ( LOOKAHEAD(2) "[" "]" {jjtThis.bumpArrayDepth();} )*
|
( "[" "]" {jjtThis.bumpArrayDepth();})+ ArrayInitializer()
}
@@ -2588,7 +2600,7 @@ void MemberValue():
void MemberValueArrayInitializer():
{}
{
"{" (MemberValue() ( LOOKAHEAD(2) "," MemberValue() )* [ "," ])? "}"
"{" (MemberValue() ( LOOKAHEAD(2) "," MemberValue() )*)? [ "," ] "}"
}
/*
@@ -46,7 +46,7 @@ public class ASTInstanceOfExpression extends AbstractJavaTypeNode {
* Gets the type against which the expression is tested.
*/
public ASTType getTypeNode() {
JavaNode child = getChild(1);
JavaNode child = getChild(getNumChildren() - 1);
return child instanceof ASTType ? (ASTType) child
: ((ASTTypePattern) child).getTypeNode();
}
@@ -5,6 +5,7 @@
package net.sourceforge.pmd.lang.java.ast;
import java.util.List;
import java.util.Objects;
import net.sourceforge.pmd.annotation.Experimental;
@@ -48,7 +49,7 @@ public final class ASTTypePattern extends AbstractJavaAnnotatableNode implements
* Gets the type against which the expression is tested.
*/
public ASTType getTypeNode() {
return getFirstChildOfType(ASTType.class);
return Objects.requireNonNull(getFirstChildOfType(ASTType.class));
}
/** Returns the declared variable. */
@@ -111,7 +111,7 @@ public abstract class AbstractJUnitRule extends AbstractJavaRule {
public static boolean isTestClass(ASTClassOrInterfaceBody node) {
return !isAbstractClass(node) && node.getParent() instanceof ASTClassOrInterfaceDeclaration
&& (isTestClassJUnit3(node) || isTestClassJUnit4(node) || isTestClassJUnit5(node));
&& (isTestClassJUnit3(node) || isTestClassJUnit4(node) || isTestClassJUnit5(node) || isTestClassTestNg(node));
}
private static boolean isAbstractClass(ASTClassOrInterfaceBody node) {
@@ -149,6 +149,14 @@ public abstract class AbstractJUnitRule extends AbstractJavaRule {
return false;
}
private static boolean isTestClassTestNg(ASTClassOrInterfaceBody node) {
Node parent = node.getParent();
if (parent instanceof TypeNode) {
TypeNode type = (TypeNode) parent;
return doesNodeContainJUnitAnnotation(type, TESTNG_ANNOTATION);
}
return false;
}
public static boolean isTestMethod(ASTMethodDeclaration method) {
if (method.isAbstract() || method.isNative() || method.isStatic()) {
@@ -11,12 +11,14 @@ import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeBodyDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeBodyDeclaration.DeclarationKind;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration.TypeKind;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceBody;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTInitializer;
import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration;
import net.sourceforge.pmd.lang.java.ast.AccessNode;
import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil;
import net.sourceforge.pmd.lang.java.rule.AbstractJUnitRule;
import net.sourceforge.pmd.properties.PropertyDescriptor;
@@ -31,6 +33,9 @@ public class ClassNamingConventionsRule extends AbstractNamingConventionRule<AST
private final PropertyDescriptor<Pattern> enumerationRegex = defaultProp("enum").build();
private final PropertyDescriptor<Pattern> annotationRegex = defaultProp("annotation").build();
private final PropertyDescriptor<Pattern> utilityClassRegex = defaultProp("utility class").build();
private final PropertyDescriptor<Pattern> testClassRegex = defaultProp("test class")
.desc("Regex which applies to test class names. Since PMD 6.52.0.")
.defaultValue("^Test.*$|^[A-Z][a-zA-Z0-9]*Test(s|Case)?$").build();
public ClassNamingConventionsRule() {
@@ -40,6 +45,7 @@ public class ClassNamingConventionsRule extends AbstractNamingConventionRule<AST
definePropertyDescriptor(enumerationRegex);
definePropertyDescriptor(annotationRegex);
definePropertyDescriptor(utilityClassRegex);
definePropertyDescriptor(testClassRegex);
addRuleChainVisit(ASTClassOrInterfaceDeclaration.class);
addRuleChainVisit(ASTEnumDeclaration.class);
@@ -108,12 +114,17 @@ public class ClassNamingConventionsRule extends AbstractNamingConventionRule<AST
&& String[].class.equals(decl.getFormalParameters().iterator().next().getType());
}
private boolean isTestClass(ASTClassOrInterfaceDeclaration node) {
return !node.isNested() && AbstractJUnitRule.isTestClass(node.getFirstChildOfType(ASTClassOrInterfaceBody.class));
}
@Override
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
if (node.isAbstract()) {
checkMatches(node, abstractClassRegex, data);
} else if (isTestClass(node)) {
checkMatches(node, testClassRegex, data);
} else if (isUtilityClass(node)) {
checkMatches(node, utilityClassRegex, data);
} else if (node.isInterface()) {
@@ -66,6 +66,7 @@ public class CommentDefaultAccessModifierRule extends AbstractIgnoredAnnotationR
ignoredStrings.add("org.junit.jupiter.api.BeforeAll");
ignoredStrings.add("org.junit.jupiter.api.AfterEach");
ignoredStrings.add("org.junit.jupiter.api.AfterAll");
ignoredStrings.add("lombok.Value");
return ignoredStrings;
}
@@ -140,7 +141,7 @@ public class CommentDefaultAccessModifierRule extends AbstractIgnoredAnnotationR
boolean isConcreteClass = parentClassOrInterface.getTypeKind() == ASTAnyTypeDeclaration.TypeKind.CLASS;
// ignore if it's inside an interface / Annotation
return isConcreteClass && isMissingComment(decl);
return isConcreteClass && isMissingComment(decl) && !hasIgnoredAnnotation(parentClassOrInterface);
}
protected boolean hasIgnoredAnnotation(AccessNode node) {
@@ -242,11 +242,16 @@ public class InvalidLogMessageFormatRule extends AbstractJavaRule {
varName = name.getImage();
}
}
if (varName == null) {
return false;
}
Scope scope = prefix == null ? null : prefix.getScope();
while (scope != null) {
// Try recursively to find the expected NameDeclaration
for (NameDeclaration decl : scope.getDeclarations().keySet()) {
if (decl.getName().equals(varName)) {
// anonymous classes have no names, so decl.getName() can be null
if (varName.equals(decl.getName())) {
// If the last parameter is a lambda parameter, then we also ignore it - regardless of the type.
// This is actually a workaround, since type resolution doesn't resolve the types of lambda parameters.
return decl.getNode().getParent() instanceof ASTLambdaExpression;
@@ -398,7 +398,7 @@ public class Foo extends Bar{
specific kind (e.g. enum or interface). Each regex can be configured through
properties.
By default this rule uses the standard Java naming convention (Pascal case).
By default, this rule uses the standard Java naming convention (Pascal case).
The rule can detect utility classes and enforce a different naming convention
on those. E.g. setting the property `utilityClassPattern` to
@@ -408,6 +408,10 @@ public class Foo extends Bar{
For this rule, a utility class is defined as: a concrete class that does not
inherit from a super class or implement any interface and only has static fields
or methods.
This rule detects test classes using the following convention: Test classes are top-level classes, that
either inherit from JUnit 3 TestCase or have at least one method annotated with the Test annotations from
JUnit4/5 or TestNG.
</description>
<priority>1</priority>
<example>
@@ -339,6 +339,8 @@ is invoked by a inner class.
<value>
<![CDATA[
//TypeDeclaration
(: no lombok constructor annotations :)
[not(Annotation[pmd-java:typeIs('lombok.Value')])]
/ClassOrInterfaceDeclaration
[@Final = false()]
(: at least one private constructor :)
@@ -84,6 +84,21 @@ public class JDKVersionTest {
java5.parseResource("jdk15_varargs.java");
}
@Test
public void testGenericCtorCalls() {
java5.parseResource("java5/generic_ctors.java");
}
@Test
public void testGenericSuperCtorCalls() {
java5.parseResource("java5/generic_super_ctor.java");
}
@Test
public void testAnnotArrayInitializer() {
java5.parseResource("java5/annotation_array_init.java");
}
@Test(expected = ParseException.class)
public void testVarargsShouldFailWith14() {
java4.parseResource("jdk15_varargs.java");
@@ -217,6 +232,11 @@ public class JDKVersionTest {
java8.parse("public class Foo { private void bar() { } }");
}
@Test
public final void testTypeAnnotations() {
java8.parseResource("java8/type_annotations.java");
}
@Test
public final void testNestedPrivateMethods() {
java8.parse("public interface Baz { public static class Foo { private void bar() { } } }");
@@ -148,10 +148,10 @@ public class ParserCornersTest {
public void testParsersCases18() throws Exception {
ASTCompilationUnit cu = java8.parseResource("ParserCornerCases18.java");
Assert.assertEquals(21, cu.findChildNodesWithXPath("//FormalParameter").size());
Assert.assertEquals(4,
Assert.assertEquals(24, cu.findChildNodesWithXPath("//FormalParameter").size());
Assert.assertEquals(5,
cu.findChildNodesWithXPath("//FormalParameter[@ExplicitReceiverParameter='true']").size());
Assert.assertEquals(17,
Assert.assertEquals(19,
cu.findChildNodesWithXPath("//FormalParameter[@ExplicitReceiverParameter='false']").size());
}
@@ -57,7 +57,7 @@ class ASTCatchStatementTest : ParserTestSpec({
}
child<ASTVariableDeclaratorId> {
it.image shouldBe "e"
it.name shouldBe "e"
}
listOf(ioe, aerr)
@@ -5,10 +5,9 @@
package net.sourceforge.pmd.lang.java.ast
import io.kotest.matchers.shouldBe
import net.sourceforge.pmd.lang.ast.test.shouldBe as typeShouldBe
import net.sourceforge.pmd.lang.java.ast.JavaVersion
import net.sourceforge.pmd.lang.java.ast.JavaVersion.*
import net.sourceforge.pmd.lang.java.ast.JavaVersion.J16
import java.io.IOException
import net.sourceforge.pmd.lang.ast.test.shouldBe as typeShouldBe
class ASTPatternTest : ParserTestSpec({
val typePatternsVersions = JavaVersion.since(J16)
@@ -28,7 +28,7 @@ enum class JavaVersion : Comparable<JavaVersion> {
J19, J19__PREVIEW;
/** Name suitable for use with e.g. [JavaParsingHelper.parse] */
val pmdName: String = name.removePrefix("J").replaceFirst("__", "-").replace('_', '.').toLowerCase()
val pmdName: String = name.removePrefix("J").replaceFirst("__", "-").replace('_', '.').lowercase()
val parser: JavaParsingHelper = JavaParsingHelper.WITH_PROCESSING.withDefaultVersion(pmdName)
@@ -4,15 +4,15 @@
package net.sourceforge.pmd.lang.java.ast
import io.kotest.core.config.configuration
import io.kotest.core.names.TestName
import io.kotest.core.source.sourceRef
import io.kotest.core.spec.DslDrivenSpec
import io.kotest.core.spec.style.scopes.Lifecycle
import io.kotest.core.spec.style.scopes.RootScope
import io.kotest.core.spec.style.scopes.RootTestRegistration
import io.kotest.core.test.TestCaseConfig
import io.kotest.core.test.TestContext
import io.kotest.core.spec.style.scopes.addContainer
import io.kotest.core.spec.style.scopes.addTest
import io.kotest.core.test.NestedTest
import io.kotest.core.test.TestScope
import io.kotest.core.test.TestType
import io.kotest.core.test.createTestName
import net.sourceforge.pmd.lang.ast.test.Assertions
import net.sourceforge.pmd.lang.ast.test.IntelliMarker
import io.kotest.matchers.should as kotlintestShould
@@ -31,22 +31,16 @@ abstract class ParserTestSpec(body: ParserTestSpec.() -> Unit) : DslDrivenSpec()
body()
}
override fun lifecycle(): Lifecycle = Lifecycle.from(this)
override fun defaultConfig(): TestCaseConfig = actualDefaultConfig()
override fun defaultTestCaseConfig(): TestCaseConfig? = defaultTestConfig
override fun registration(): RootTestRegistration = RootTestRegistration.from(this)
private fun actualDefaultConfig() =
defaultTestConfig ?: defaultTestCaseConfig() ?: configuration.defaultTestConfig
fun test(name: String, disabled: Boolean = false, test: suspend TestContext.() -> Unit) =
registration().addTest(
name = createTestName(name),
xdisabled = disabled,
test = test,
config = actualDefaultConfig()
fun test(name: String, disabled: Boolean = false, test: suspend TestScope.() -> Unit) =
addTest(
testName = TestName(name),
disabled = disabled,
config = null,
type = TestType.Test,
test = test
)
/**
* Defines a group of tests that should be named similarly,
* with separate tests for separate versions.
@@ -66,10 +60,11 @@ abstract class ParserTestSpec(body: ParserTestSpec.() -> Unit) : DslDrivenSpec()
fun parserTestGroup(name: String,
disabled: Boolean = false,
spec: suspend GroupTestCtx.() -> Unit) =
registration().addContainerTest(
name = createTestName(name),
addContainer(
testName = TestName(name),
test = { GroupTestCtx(this).spec() },
xdisabled = disabled
disabled = disabled,
config = null
)
/**
@@ -117,37 +112,43 @@ abstract class ParserTestSpec(body: ParserTestSpec.() -> Unit) : DslDrivenSpec()
}
private suspend fun containedParserTestImpl(
context: TestContext,
scope: TestScope,
name: String,
javaVersion: JavaVersion,
assertions: ParserTestCtx.() -> Unit) {
context.registerTestCase(
name = createTestName(name),
test = { ParserTestCtx(javaVersion).assertions() },
config = actualDefaultConfig(),
type = TestType.Test
val nested = NestedTest(
name = TestName(name),
test = { ParserTestCtx(javaVersion).assertions() },
config = null,
type = TestType.Test,
disabled = false,
source = sourceRef()
)
scope.registerTestCase(nested)
}
inner class GroupTestCtx(private val context: TestContext) {
inner class GroupTestCtx(private val scope: TestScope) {
suspend fun onVersions(javaVersions: List<JavaVersion>, spec: suspend VersionedTestCtx.() -> Unit) {
javaVersions.forEach { javaVersion ->
context.registerTestCase(
name = createTestName("Java ${javaVersion.pmdName}"),
test = { VersionedTestCtx(this, javaVersion).spec() },
config = actualDefaultConfig(),
type = TestType.Container
val nested = NestedTest(
name = TestName("Java ${javaVersion.pmdName}"),
test = { VersionedTestCtx(this, javaVersion).spec() },
config = null,
type = TestType.Container,
disabled = false,
source = sourceRef()
)
scope.registerTestCase(nested)
}
}
inner class VersionedTestCtx(private val context: TestContext, javaVersion: JavaVersion) : ParserTestCtx(javaVersion) {
inner class VersionedTestCtx(private val scope: TestScope, javaVersion: JavaVersion) : ParserTestCtx(javaVersion) {
suspend infix fun String.should(matcher: Assertions<String>) {
containedParserTestImpl(context, "'$this'", javaVersion = javaVersion) {
containedParserTestImpl(scope, "'$this'", javaVersion = javaVersion) {
this@should kotlintestShould matcher
}
}
Loaded 30 of 42 files, more files were not shown because too many files have changed in this diff. Show more