Merge branch 'pmd/7.0.x' into pmd7-replace-commons-io

This commit is contained in:
Andreas Dangel
2022-05-26 15:04:34 +02:00
38 changed files with 1294 additions and 169 deletions
+36
View File
@@ -6639,6 +6639,42 @@
"contributions": [
"code"
]
},
{
"login": "lukelukes",
"name": "lukelukes",
"avatar_url": "https://avatars.githubusercontent.com/u/45536418?v=4",
"profile": "https://github.com/lukelukes",
"contributions": [
"code"
]
},
{
"login": "vibhory2j",
"name": "Vibhor Goyal",
"avatar_url": "https://avatars.githubusercontent.com/u/15845016?v=4",
"profile": "https://github.com/vibhory2j",
"contributions": [
"bug"
]
},
{
"login": "Ramel0921",
"name": "Ramel0921",
"avatar_url": "https://avatars.githubusercontent.com/u/104978096?v=4",
"profile": "https://github.com/Ramel0921",
"contributions": [
"bug"
]
},
{
"login": "flyhard",
"name": "Per Abich",
"avatar_url": "https://avatars.githubusercontent.com/u/409466?v=4",
"profile": "https://github.com/flyhard",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
+8 -2
View File
@@ -14,5 +14,11 @@ last_updated: April 2022 (6.45.0)
The HTML language module uses [jsoup](https://jsoup.org/) for parsing.
XPath rules are supported, but the DOM is not a typical XML/XPath DOM. E.g.
text nodes are normal nodes. This might change in the future.
XPath 2.0 rules are supported, but the DOM is not always a typical XML/XPath DOM.
In the Designer, text nodes appear as nodes with name "#text", but they can
be selected as usual using `text()`.
XML Namespaces are not supported. The local name of attributes include the prefix,
so that you have to select attributes by e.g. `//*[@*[local-name() = 'if:true']]`.
Only XPath 1.0 rules are not supported.
File diff suppressed because it is too large Load Diff
+5 -5
View File
@@ -76,14 +76,14 @@ The tool comes with a rather extensive help text, simply running with `--help`!
%}
{% include custom/cli_option_row.html options="--file-list"
option_arg="filepath"
description="Path to file containing a comma delimited list of files to analyze.
description="Path to file containing a list of files to analyze, one path per line.
If this is given, then you don't need to provide `--dir`."
%}
{% include custom/cli_option_row.html options="--force-language"
option_arg="lang"
description="Force a language to be used for all input files, irrespective of
filenames. When using this option, the automatic language selection
by extension is disabled and all files are tried to be parsed with
file names. When using this option, the automatic language selection
by extension is disabled and PMD tries to parse all files with
the given language `<lang>`. Parsing errors are ignored and unparsable files
are skipped.
@@ -92,9 +92,9 @@ The tool comes with a rather extensive help text, simply running with `--help`!
%}
{% include custom/cli_option_row.html options="--ignore-list"
option_arg="filepath"
description="Path to file containing a comma delimited list of files to ignore.
description="Path to file containing a list of files to ignore, one path per line.
This option can be combined with `--dir` and `--file-list`.
This ignore list takes precedence over any files in the filelist."
This ignore list takes precedence over any files in the file-list."
%}
{% include custom/cli_option_row.html options="--help,-h,-H"
description="Display help on usage."
+2 -2
View File
@@ -112,9 +112,9 @@ Novice as much as advanced readers may want to [read on on Refactoring Guru](htt
languages="Java"
%}
{% include custom/cli_option_row.html options="--ignore-annotations"
description="Ignore language annotations when comparing text"
description="Ignore language annotations (Java) or attributes (C#) when comparing text"
default="false"
languages="Java"
languages="C#, Java"
%}
{% include custom/cli_option_row.html options="--ignore-literal-sequences"
description="Ignore sequences of literals (common e.g. in list initializers)"
+49 -1
View File
@@ -19,15 +19,63 @@ This is a {{ site.pmd.release_type }} release.
### New and noteworthy
#### CLI improvements
The PMD CLI now allows repeating the `--dir` (`-d`) and `--rulesets` (`-R`) options,
as well as providing several space-separated arguments to either of them. For instance:
```shell
pmd -d src/main/java src/test/java -R rset1.xml -R rset2.xml
```
This also allows globs to be used on the CLI if your shell supports shell expansion.
For instance, the above can be written
```shell
pmd -d src/*/java -R rset*.xml
```
Please use theses new forms instead of using comma-separated lists as argument to these options.
#### C# Improvements
When executing CPD on C# sources, the option `--ignore-annotations` is now supported as well.
It ignores C# attributes when detecting duplicated code. This option can also be enabled via
the CPD GUI. See [#3974](https://github.com/pmd/pmd/pull/3974) for details.
### Fixed Issues
* core
* [#3942](https://github.com/pmd/pmd/issues/3942): \[core] common-io path traversal vulnerability (CVE-2021-29425)
* cli
* [#1445](https://github.com/pmd/pmd/issues/1445): \[core] Allow CLI to take globs as parameters
* cs (c#)
* [#3974](https://github.com/pmd/pmd/pull/3974): \[cs] Add option to ignore C# attributes (annotations)
* go
* [#2752](https://github.com/pmd/pmd/issues/2752): \[go] Error parsing unicode values
* html
* [#3955](https://github.com/pmd/pmd/pull/3955): \[html] Improvements for handling text and comment nodes
* java
* [#3423](https://github.com/pmd/pmd/issues/3423): \[java] Error processing identifiers with Unicode
* java-bestpractices
* [#3954](https://github.com/pmd/pmd/issues/3954): \[java] NPE in UseCollectionIsEmptyRule when .size() is called in a record
* java-design
* [#3874](https://github.com/pmd/pmd/issues/3874): \[java] ImmutableField reports fields annotated with @Autowired (Spring) and @Mock (Mockito)
* javascript
* [#3948](https://github.com/pmd/pmd/issues/3948): \[js] Invalid operator error for method property in object literal
* [#2605](https://github.com/pmd/pmd/issues/2605): \[js] Support unicode characters
* [#3948](https://github.com/pmd/pmd/issues/3948): \[js] Invalid operator error for method property in object literal
* python
* [#2604](https://github.com/pmd/pmd/issues/2604): \[python] Support unicode identifiers
### API Changes
#### Deprecated API
- {% jdoc core::PMDConfiguration#getInputPaths() %} and
{% jdoc core::PMDConfiguration#setInputPaths(java.lang.String) %} are now deprecated.
A new set of methods have been added, which use lists and do not rely on comma splitting.
### External Contributions
* [#3961](https://github.com/pmd/pmd/pull/3961): \[java] Fix #3954 - NPE in UseCollectionIsEmptyRule with record - [@flyhard](https://github.com/flyhard)
* [#3964](https://github.com/pmd/pmd/pull/3964): \[java] Fix #3874 - ImmutableField: fix mockito/spring false positives - [@lukelukes](https://github.com/lukelukes)
* [#3974](https://github.com/pmd/pmd/pull/3974): \[cs] Add option to ignore C# attributes (annotations) - [@maikelsteneker](https://github.com/maikelsteneker)
{% endtocmaker %}
@@ -8,10 +8,12 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -104,7 +106,7 @@ public class PMDConfiguration extends AbstractConfiguration {
// Rule and source file options
private List<String> ruleSets = new ArrayList<>();
private RulePriority minimumPriority = RulePriority.LOW;
private String inputPaths;
private List<String> inputPaths = new ArrayList<>();
private String inputUri;
private String inputFilePath;
private String ignoreFilePath;
@@ -435,19 +437,54 @@ public class PMDConfiguration extends AbstractConfiguration {
* Get the comma separated list of input paths to process for source files.
*
* @return A comma separated list.
*
* @deprecated Use {@link #getAllInputPaths()}
*/
@Deprecated
public String getInputPaths() {
return inputPaths;
return inputPaths.isEmpty() ? null : StringUtils.join(inputPaths, ",");
}
/**
* Returns an unmodifiable list.
*
* @throws NullPointerException If the parameter is null
*/
public List<String> getAllInputPaths() {
return Collections.unmodifiableList(inputPaths);
}
/**
* Set the comma separated list of input paths to process for source files.
*
* @param inputPaths
* The comma separated list.
* @param inputPaths The comma separated list.
*
* @throws NullPointerException If the parameter is null
* @deprecated Use {@link #setInputPaths(List)} or {@link #addInputPath(String)}
*/
@Deprecated
public void setInputPaths(String inputPaths) {
this.inputPaths = inputPaths;
List<String> paths = new ArrayList<>();
Collections.addAll(paths, inputPaths.split(","));
this.inputPaths = paths;
}
/**
* Set the input paths to the given list of paths.
* @throws NullPointerException If the parameter is null
*/
public void setInputPaths(List<String> inputPaths) {
this.inputPaths = new ArrayList<>(inputPaths);
}
/**
* Add an input path. It is not split on commas.
*
* @throws NullPointerException If the parameter is null
*/
public void addInputPath(String inputPath) {
Objects.requireNonNull(inputPath);
this.inputPaths.add(inputPath);
}
public String getInputFilePath() {
@@ -541,7 +578,7 @@ public class PMDConfiguration extends AbstractConfiguration {
Renderer renderer = RendererFactory.createRenderer(reportFormat, reportProperties);
renderer.setShowSuppressedViolations(showSuppressedViolations);
if (reportShortNames && inputPaths != null) {
renderer.setUseShortNames(Arrays.asList(inputPaths.split(",")));
renderer.setUseShortNames(Collections.unmodifiableList(new ArrayList<>(inputPaths)));
}
if (withReportWriter) {
renderer.setReportFile(reportFile);
@@ -9,6 +9,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.PMD;
@@ -32,20 +33,44 @@ import com.beust.jcommander.validators.PositiveInteger;
@InternalApi
public class PMDParameters {
@Parameter(names = { "--rulesets", "-rulesets", "-R" }, description = "Comma separated list of ruleset names to use.",
required = true)
private String rulesets;
@Parameter(names = { "--rulesets", "-rulesets", "-R" },
description = "Path to a ruleset xml file. "
+ "The path may reference a resource on the classpath of the application, be a local file system path, or a URL. "
+ "The option can be repeated, and multiple arguments can be provided to a single occurrence of the option.",
required = true,
variableArity = true)
private List<String> rulesets;
@Parameter(names = { "--uri", "-uri", "-u" }, description = "Database URI for sources.")
@Parameter(names = { "--uri", "-uri", "-u" },
description = "Database URI for sources. "
+ "One of --dir, --file-list or --uri must be provided. "
)
private String uri;
@Parameter(names = { "--dir", "-dir", "-d" }, description = "Root directory for sources.")
private String sourceDir;
@Parameter(names = { "--dir", "-dir", "-d" },
description = "Path to a source file, or directory containing source files to analyze. "
// About the following line:
// In PMD 6, this is only the case for files found in directories. If you
// specify a file directly, and it is unknown, then the Java parser is used.
+ "Note that a file is only effectively added if it matches a language known by PMD. "
+ "Zip and Jar files are also supported, if they are specified directly "
+ "(archive files found while exploring a directory are not recursively expanded). "
+ "This option can be repeated, and multiple arguments can be provided to a single occurrence of the option. "
+ "One of --dir, --file-list or --uri must be provided. ",
variableArity = true)
private List<String> inputPaths = new ArrayList<>();
@Parameter(names = { "--file-list", "-filelist" }, description = "Path to a file containing a list of files to analyze.")
@Parameter(names = { "--file-list", "-filelist" },
description =
"Path to a file containing a list of files to analyze, one path per line. "
+ "One of --dir, --file-list or --uri must be provided. "
)
private String fileListPath;
@Parameter(names = { "--ignore-list", "-ignorelist" }, description = "Path to a file containing a list of files to ignore.")
@Parameter(names = { "--ignore-list", "-ignorelist" },
description = "Path to a file containing a list of files to exclude from the analysis, one path per line. "
+ "This option can be combined with --dir and --file-list. "
)
private String ignoreListPath;
@Parameter(names = { "--format", "-format", "-f" }, description = "Report format type.")
@@ -107,12 +132,17 @@ public class PMDParameters {
@Parameter(names = { "-language", "-l" }, description = "Specify a language PMD should use.")
private String language = null;
@Parameter(names = { "--force-language", "-force-language" }, description = "Force a language to be used for all input files, irrespective of filenames.")
@Parameter(names = { "--force-language", "-force-language" },
description = "Force a language to be used for all input files, irrespective of file names. "
+ "When using this option, the automatic language selection by extension is disabled, and PMD "
+ "tries to parse all input files with the given language's parser. "
+ "Parsing errors are ignored."
)
private String forceLanguage = null;
@Parameter(names = { "--aux-classpath", "-auxclasspath" },
description = "Specifies the classpath for libraries used by the source code. "
+ "This is used by the type resolution. The platform specific path delimiter "
+ "This is used to resolve types in Java source files. The platform specific path delimiter "
+ "(\":\" on Linux, \";\" on Windows) is used to separate the entries. "
+ "Alternatively, a single 'file:' URL to a text file containing path elements on consecutive lines "
+ "can be specified.")
@@ -199,10 +229,6 @@ public class PMDParameters {
* @throws IllegalArgumentException if the parameters are inconsistent or incomplete
*/
public PMDConfiguration toConfiguration() {
if (null == this.getSourceDir() && null == this.getUri() && null == this.getFileListPath()) {
throw new IllegalArgumentException(
"Please provide a parameter for source root directory (-dir or -d), database URI (-uri or -u), or file list path (-filelist).");
}
PMDConfiguration configuration = new PMDConfiguration();
configuration.setInputPaths(this.getSourceDir());
configuration.setInputFilePath(this.getFileListPath());
@@ -348,12 +374,22 @@ public class PMDParameters {
return auxclasspath;
}
@Deprecated
public String getRulesets() {
return StringUtils.join(rulesets, ",");
}
public List<String> getRulesetRefs() {
return rulesets;
}
public List<String> getInputPaths() {
return inputPaths;
}
@Deprecated
public String getSourceDir() {
return sourceDir;
return StringUtils.join(inputPaths, ",");
}
public String getFileListPath() {
@@ -105,13 +105,29 @@ public final class PmdParametersParseResult {
jcommander.setProgramName("pmd");
try {
jcommander.parse(args);
parseAndValidate(jcommander, result, args);
return new PmdParametersParseResult(result, filterDeprecatedOptions(args));
} catch (ParameterException e) {
return new PmdParametersParseResult(e, filterDeprecatedOptions(args));
}
}
private static void parseAndValidate(JCommander jcommander, PMDParameters result, String[] args) {
jcommander.parse(args);
if (result.isHelp() || result.isVersion()) {
return;
}
// jcommander has no special support for global parameter validation like this
// For consistency we report this with a ParameterException
if (result.getInputPaths().isEmpty()
&& null == result.getUri()
&& null == result.getFileListPath()) {
throw new ParameterException(
"Please provide a parameter for source root directory (--dir or -d), database URI (--uri or -u), or file list path (--file-list).");
}
}
private static Map<String, String> filterDeprecatedOptions(String... args) {
Map<String, String> argSet = new LinkedHashMap<>(SUGGESTED_REPLACEMENT);
argSet.keySet().retainAll(new HashSet<>(Arrays.asList(args)));
@@ -147,7 +147,16 @@ public class GUI implements CPDListener {
@Override
public boolean canIgnoreAnnotations() {
return "java".equals(terseName);
if (terseName == null) {
return false;
}
switch (terseName) {
case "cs":
case "java":
return true;
default:
return false;
}
}
@Override
@@ -52,7 +52,7 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin
BaseNodeInfo parent,
Node wrappedNode,
Configuration configuration) {
super(Type.ELEMENT, configuration.getNamePool(), wrappedNode.getXPathNodeName(), parent);
super(determineType(wrappedNode), configuration.getNamePool(), wrappedNode.getXPathNodeName(), parent);
this.treeInfo = document;
this.wrappedNode = wrappedNode;
@@ -65,6 +65,19 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin
}
}
private static int determineType(Node node) {
// As of PMD 6.48.0, only the experimental HTML module uses this naming
// convention to identify non-element nodes.
// TODO PMD 7: maybe generalize this to other languages
String name = node.getXPathNodeName();
if ("#text".equals(name)) {
return Type.TEXT;
} else if ("#comment".equals(name)) {
return Type.COMMENT;
}
return Type.ELEMENT;
}
public Map<String, AstAttributeNode> makeAttributes(Node wrappedNode) {
Map<String, AstAttributeNode> atts = new HashMap<>();
Iterator<Attribute> it = wrappedNode.getXPathAttributesIterator();
@@ -179,6 +192,10 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin
@Override
public CharSequence getStringValueCS() {
if (getNodeKind() == Type.TEXT || getNodeKind() == Type.COMMENT) {
return getUnderlyingNode().getImage();
}
// https://www.w3.org/TR/xpath-datamodel-31/#ElementNode
// The string-value property of an Element Node must be the
// concatenation of the string-values of all its Text Node
@@ -187,6 +204,7 @@ public final class AstElementNode extends BaseNodeInfo implements SiblingCountin
// Since we represent all our Nodes as elements, there are no
// text nodes
// TODO: for some languages like html we have text nodes
return "";
}
@@ -4,10 +4,17 @@
package net.sourceforge.pmd.cli;
import static net.sourceforge.pmd.util.CollectionUtil.listOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.Assert;
import org.junit.Test;
import net.sourceforge.pmd.PMDConfiguration;
public class PMDParametersTest {
@Test
@@ -20,4 +27,60 @@ public class PMDParametersTest {
FieldUtils.writeDeclaredField(parameters, "language", "dummy2", true);
Assert.assertEquals("1.0", parameters.getVersion());
}
@Test
public void testMultipleDirsAndRuleSets() {
PmdParametersParseResult result = PmdParametersParseResult.extractParameters(
"-d", "a", "b", "-R", "x.xml", "y.xml"
);
assertMultipleDirsAndRulesets(result);
}
@Test
public void testMultipleDirsAndRuleSetsWithCommas() {
PmdParametersParseResult result = PmdParametersParseResult.extractParameters(
"-d", "a,b", "-R", "x.xml,y.xml"
);
assertMultipleDirsAndRulesets(result);
}
@Test
public void testMultipleDirsAndRuleSetsWithRepeatedOption() {
PmdParametersParseResult result = PmdParametersParseResult.extractParameters(
"-d", "a", "-d", "b", "-R", "x.xml", "-R", "y.xml"
);
assertMultipleDirsAndRulesets(result);
}
@Test
public void testNoPositionalParametersAllowed() {
assertError(
// vvvv
"-R", "x.xml", "-d", "a", "--", "-d", "b"
);
}
private void assertMultipleDirsAndRulesets(PmdParametersParseResult result) {
assertFalse(result.isError());
PMDConfiguration config = result.toConfiguration();
assertEquals(config.getAllInputPaths(), listOf("a", "b"));
assertEquals(config.getRuleSetPaths(), listOf("x.xml", "y.xml"));
}
@Test
public void testEmptyDirOption() {
assertError("-d", "-R", "y.xml");
}
@Test
public void testEmptyRulesetOption() {
assertError("-R", "-d", "something");
}
private void assertError(String... params) {
PmdParametersParseResult result = PmdParametersParseResult.extractParameters(params);
assertTrue(result.isError());
}
}
@@ -57,4 +57,54 @@ public class ElementNodeTest {
Assert.assertEquals(0, elementFoo1.compareOrder(elementFoo1));
}
@Test
public void verifyTextNodeType() {
DummyRootNode root = new DummyRootNode();
DummyNode c0 = new DummyNode(false, "foo");
c0.setCoords(1, 1, 2, 2);
root.addChild(c0, 0);
DummyNode c1 = new DummyNode(false, "#text");
c1.setCoords(2, 1, 2, 2);
root.addChild(c1, 1);
Configuration configuration = Configuration.newConfiguration();
AstTreeInfo treeInfo = new AstTreeInfo(root, configuration);
AstElementNode rootElt = treeInfo.getRootNode().getRootElement();
Assert.assertSame(root, rootElt.getUnderlyingNode());
Assert.assertEquals(Type.ELEMENT, rootElt.getNodeKind());
Assert.assertSame(rootElt, treeInfo.findWrapperFor(root));
AstElementNode elementFoo0 = rootElt.getChildren().get(0);
Assert.assertEquals(Type.ELEMENT, elementFoo0.getNodeKind());
Assert.assertSame(c0, elementFoo0.getUnderlyingNode());
Assert.assertSame(elementFoo0, treeInfo.findWrapperFor(c0));
AstElementNode elementText1 = rootElt.getChildren().get(1);
Assert.assertEquals(Type.TEXT, elementText1.getNodeKind());
Assert.assertSame(c1, elementText1.getUnderlyingNode());
Assert.assertSame(elementText1, treeInfo.findWrapperFor(c1));
}
@Test
public void verifyCommentNodeType() {
DummyRootNode root = new DummyRootNode();
DummyNode c1 = new DummyNode(false, "#comment");
c1.setCoords(2, 1, 2, 2);
root.addChild(c1, 0);
Configuration configuration = Configuration.newConfiguration();
AstTreeInfo treeInfo = new AstTreeInfo(root, configuration);
AstElementNode rootElt = treeInfo.getRootNode().getRootElement();
AstElementNode elementComment = rootElt.getChildren().get(0);
Assert.assertEquals(Type.COMMENT, elementComment.getNodeKind());
Assert.assertSame(c1, elementComment.getUnderlyingNode());
Assert.assertSame(elementComment, treeInfo.findWrapperFor(c1));
}
}
@@ -21,6 +21,7 @@ public class CsTokenizer extends AntlrTokenizer {
private boolean ignoreUsings = false;
private boolean ignoreLiteralSequences = false;
private boolean ignoreAttributes = false;
/**
* Sets the possible options for the C# tokenizer.
@@ -28,19 +29,16 @@ public class CsTokenizer extends AntlrTokenizer {
* @param properties the properties
* @see #IGNORE_USINGS
* @see #OPTION_IGNORE_LITERAL_SEQUENCES
* @see #IGNORE_ANNOTATIONS
*/
public void setProperties(Properties properties) {
ignoreUsings = Boolean.parseBoolean(properties.getProperty(IGNORE_USINGS, Boolean.FALSE.toString()));
ignoreLiteralSequences = Boolean.parseBoolean(properties.getProperty(OPTION_IGNORE_LITERAL_SEQUENCES,
Boolean.FALSE.toString()));
ignoreUsings = getBooleanProperty(properties, IGNORE_USINGS);
ignoreLiteralSequences = getBooleanProperty(properties, OPTION_IGNORE_LITERAL_SEQUENCES);
ignoreAttributes = getBooleanProperty(properties, IGNORE_ANNOTATIONS);
}
public void setIgnoreUsings(boolean ignoreUsings) {
this.ignoreUsings = ignoreUsings;
}
public void setIgnoreLiteralSequences(boolean ignoreLiteralSequences) {
this.ignoreLiteralSequences = ignoreLiteralSequences;
private boolean getBooleanProperty(final Properties properties, final String property) {
return Boolean.parseBoolean(properties.getProperty(property, Boolean.FALSE.toString()));
}
@Override
@@ -51,7 +49,7 @@ public class CsTokenizer extends AntlrTokenizer {
@Override
protected AntlrTokenFilter getTokenFilter(final AntlrTokenManager tokenManager) {
return new CsTokenFilter(tokenManager, ignoreUsings, ignoreLiteralSequences);
return new CsTokenFilter(tokenManager, ignoreUsings, ignoreLiteralSequences, ignoreAttributes);
}
/**
@@ -70,15 +68,18 @@ public class CsTokenizer extends AntlrTokenizer {
private final boolean ignoreUsings;
private final boolean ignoreLiteralSequences;
private final boolean ignoreAttributes;
private boolean discardingUsings = false;
private boolean discardingNL = false;
private boolean isDiscardingAttribute = false;
private AntlrToken discardingLiteralsUntil = null;
private boolean discardCurrent = false;
CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences) {
CsTokenFilter(final AntlrTokenManager tokenManager, boolean ignoreUsings, boolean ignoreLiteralSequences, boolean ignoreAttributes) {
super(tokenManager);
this.ignoreUsings = ignoreUsings;
this.ignoreLiteralSequences = ignoreLiteralSequences;
this.ignoreAttributes = ignoreAttributes;
}
@Override
@@ -91,6 +92,7 @@ public class CsTokenizer extends AntlrTokenizer {
discardCurrent = false;
skipUsingDirectives(currentToken, remainingTokens);
skipLiteralSequences(currentToken, remainingTokens);
skipAttributes(currentToken);
}
private void skipUsingDirectives(final AntlrToken currentToken, final Iterable<AntlrToken> remainingTokens) {
@@ -167,6 +169,25 @@ public class CsTokenizer extends AntlrTokenizer {
discardingNL = currentToken.getKind() == CSharpLexer.NL;
}
private void skipAttributes(final AntlrToken currentToken) {
if (ignoreAttributes) {
switch (currentToken.getKind()) {
case CSharpLexer.OPEN_BRACKET:
// Start of an attribute.
isDiscardingAttribute = true;
break;
case CSharpLexer.CLOSE_BRACKET:
// End of an attribute.
isDiscardingAttribute = false;
discardCurrent = true;
break;
default:
// Skip any other token.
break;
}
}
}
private void skipLiteralSequences(final AntlrToken currentToken, final Iterable<AntlrToken> remainingTokens) {
if (ignoreLiteralSequences) {
final int type = currentToken.getKind();
@@ -222,7 +243,7 @@ public class CsTokenizer extends AntlrTokenizer {
@Override
protected boolean isLanguageSpecificDiscarding() {
return discardingUsings || discardingNL || isDiscardingLiterals() || discardCurrent;
return discardingUsings || discardingNL || isDiscardingAttribute || isDiscardingLiterals() || discardCurrent;
}
}
}
@@ -105,18 +105,33 @@ public class CsTokenizerTest extends CpdTextComparisonTest {
doTest("csharp7And8Additions");
}
@Test
public void testAttributesAreNotIgnored() {
doTest("attributes");
}
@Test
public void testAttributesAreIgnored() {
doTest("attributes", "_ignored", skipAttributes());
}
private Properties ignoreUsings() {
return properties(true, false);
return properties(true, false, false);
}
private Properties skipLiteralSequences() {
return properties(false, true);
return properties(false, true, false);
}
private Properties properties(boolean ignoreUsings, boolean ignoreLiteralSequences) {
private Properties skipAttributes() {
return properties(false, false, true);
}
private Properties properties(boolean ignoreUsings, boolean ignoreLiteralSequences, boolean ignoreAttributes) {
Properties properties = new Properties();
properties.setProperty(Tokenizer.IGNORE_USINGS, Boolean.toString(ignoreUsings));
properties.setProperty(Tokenizer.OPTION_IGNORE_LITERAL_SEQUENCES, Boolean.toString(ignoreLiteralSequences));
properties.setProperty(Tokenizer.IGNORE_ANNOTATIONS, Boolean.toString(ignoreAttributes));
return properties;
}
}
@@ -0,0 +1,42 @@
[Serializable]
public class SampleClass
{
// Objects of this type can be serialized.
}
[System.Runtime.InteropServices.DllImport("user32.dll")]
extern static void SampleMethod();
void MethodA([In][Out] ref double x) { }
void MethodB([Out][In] ref double x) { }
void MethodC([In, Out] ref double x) { }
[Conditional("DEBUG"), Conditional("TEST1")]
void TraceMethod()
{
// ...
}
[DllImport("user32.dll")]
[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)]
using System;
using System.Reflection;
[assembly: AssemblyTitleAttribute("Production assembly 4")]
[module: CLSCompliant(true)]
// default: applies to method
[ValidatedContract]
int Method1() { return 0; }
// applies to method
[method: ValidatedContract]
int Method2() { return 0; }
// applies to parameter
int Method3([ValidatedContract] string contract) { return 0; }
// applies to return value
[return: ValidatedContract]
int Method4() { return 0; }
@@ -0,0 +1,229 @@
[Image] or [Truncated image[ Bcol Ecol
L1
[\[] 1 2
[Serializable] 2 14
[\]] 14 15
L2
[public] 1 7
[class] 8 13
[SampleClass] 14 25
L3
[{] 1 2
L5
[}] 1 2
L7
[\[] 1 2
[System] 2 8
[.] 8 9
[Runtime] 9 16
[.] 16 17
[InteropServices] 17 32
[.] 32 33
[DllImport] 33 42
[(] 42 43
["user32.dll"] 43 55
[)] 55 56
[\]] 56 57
L8
[extern] 1 7
[static] 8 14
[void] 15 19
[SampleMethod] 20 32
[(] 32 33
[)] 33 34
[;] 34 35
L10
[void] 1 5
[MethodA] 6 13
[(] 13 14
[\[] 14 15
[In] 15 17
[\]] 17 18
[\[] 18 19
[Out] 19 22
[\]] 22 23
[ref] 24 27
[double] 28 34
[x] 35 36
[)] 36 37
[{] 38 39
[}] 40 41
L11
[void] 1 5
[MethodB] 6 13
[(] 13 14
[\[] 14 15
[Out] 15 18
[\]] 18 19
[\[] 19 20
[In] 20 22
[\]] 22 23
[ref] 24 27
[double] 28 34
[x] 35 36
[)] 36 37
[{] 38 39
[}] 40 41
L12
[void] 1 5
[MethodC] 6 13
[(] 13 14
[\[] 14 15
[In] 15 17
[,] 17 18
[Out] 19 22
[\]] 22 23
[ref] 24 27
[double] 28 34
[x] 35 36
[)] 36 37
[{] 38 39
[}] 40 41
L14
[\[] 1 2
[Conditional] 2 13
[(] 13 14
["DEBUG"] 14 21
[)] 21 22
[,] 22 23
[Conditional] 24 35
[(] 35 36
["TEST1"] 36 43
[)] 43 44
[\]] 44 45
L15
[void] 1 5
[TraceMethod] 6 17
[(] 17 18
[)] 18 19
L16
[{] 1 2
L18
[}] 1 2
L20
[\[] 1 2
[DllImport] 2 11
[(] 11 12
["user32.dll"] 12 24
[)] 24 25
[\]] 25 26
L21
[\[] 1 2
[DllImport] 2 11
[(] 11 12
["user32.dll"] 12 24
[,] 24 25
[SetLastError] 26 38
[=] 38 39
[false] 39 44
[,] 44 45
[ExactSpelling] 46 59
[=] 59 60
[false] 60 65
[)] 65 66
[\]] 66 67
L22
[\[] 1 2
[DllImport] 2 11
[(] 11 12
["user32.dll"] 12 24
[,] 24 25
[ExactSpelling] 26 39
[=] 39 40
[false] 40 45
[,] 45 46
[SetLastError] 47 59
[=] 59 60
[false] 60 65
[)] 65 66
[\]] 66 67
L24
[using] 1 6
[System] 7 13
[;] 13 14
L25
[using] 1 6
[System] 7 13
[.] 13 14
[Reflection] 14 24
[;] 24 25
L26
[\[] 1 2
[assembly] 2 10
[:] 10 11
[AssemblyTitleAttribute] 12 34
[(] 34 35
["Production assembly 4"] 35 58
[)] 58 59
[\]] 59 60
L27
[\[] 1 2
[module] 2 8
[:] 8 9
[CLSCompliant] 10 22
[(] 22 23
[true] 23 27
[)] 27 28
[\]] 28 29
L30
[\[] 1 2
[ValidatedContract] 2 19
[\]] 19 20
L31
[int] 1 4
[Method1] 5 12
[(] 12 13
[)] 13 14
[{] 15 16
[return] 17 23
[0] 24 25
[;] 25 26
[}] 27 28
L34
[\[] 1 2
[method] 2 8
[:] 8 9
[ValidatedContract] 10 27
[\]] 27 28
L35
[int] 1 4
[Method2] 5 12
[(] 12 13
[)] 13 14
[{] 15 16
[return] 17 23
[0] 24 25
[;] 25 26
[}] 27 28
L38
[int] 1 4
[Method3] 5 12
[(] 12 13
[\[] 13 14
[ValidatedContract] 14 31
[\]] 31 32
[string] 33 39
[contract] 40 48
[)] 48 49
[{] 50 51
[return] 52 58
[0] 59 60
[;] 60 61
[}] 62 63
L41
[\[] 1 2
[return] 2 8
[:] 8 9
[ValidatedContract] 10 27
[\]] 27 28
L42
[int] 1 4
[Method4] 5 12
[(] 12 13
[)] 13 14
[{] 15 16
[return] 17 23
[0] 24 25
[;] 25 26
[}] 27 28
EOF
@@ -0,0 +1,109 @@
[Image] or [Truncated image[ Bcol Ecol
L2
[public] 1 7
[class] 8 13
[SampleClass] 14 25
L3
[{] 1 2
L5
[}] 1 2
L8
[extern] 1 7
[static] 8 14
[void] 15 19
[SampleMethod] 20 32
[(] 32 33
[)] 33 34
[;] 34 35
L10
[void] 1 5
[MethodA] 6 13
[(] 13 14
[ref] 24 27
[double] 28 34
[x] 35 36
[)] 36 37
[{] 38 39
[}] 40 41
L11
[void] 1 5
[MethodB] 6 13
[(] 13 14
[ref] 24 27
[double] 28 34
[x] 35 36
[)] 36 37
[{] 38 39
[}] 40 41
L12
[void] 1 5
[MethodC] 6 13
[(] 13 14
[ref] 24 27
[double] 28 34
[x] 35 36
[)] 36 37
[{] 38 39
[}] 40 41
L15
[void] 1 5
[TraceMethod] 6 17
[(] 17 18
[)] 18 19
L16
[{] 1 2
L18
[}] 1 2
L24
[using] 1 6
[System] 7 13
[;] 13 14
L25
[using] 1 6
[System] 7 13
[.] 13 14
[Reflection] 14 24
[;] 24 25
L31
[int] 1 4
[Method1] 5 12
[(] 12 13
[)] 13 14
[{] 15 16
[return] 17 23
[0] 24 25
[;] 25 26
[}] 27 28
L35
[int] 1 4
[Method2] 5 12
[(] 12 13
[)] 13 14
[{] 15 16
[return] 17 23
[0] 24 25
[;] 25 26
[}] 27 28
L38
[int] 1 4
[Method3] 5 12
[(] 12 13
[string] 33 39
[contract] 40 48
[)] 48 49
[{] 50 51
[return] 52 58
[0] 59 60
[;] 60 61
[}] 62 63
L42
[int] 1 4
[Method4] 5 12
[(] 12 13
[)] 13 14
[{] 15 16
[return] 17 23
[0] 24 25
[;] 25 26
[}] 27 28
EOF
@@ -30,6 +30,8 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest {
SUPPORTED_LANGUAGES_PMD = "apex, ecmascript, html, java, jsp, modelica, plsql, pom, scala, swift, vf, vm, wsdl, xml, xsl";
}
private final String srcDir = new File(".", "src/test/resources/sample-source/java/").getAbsolutePath();
@Test
public void testFileExistence() {
assertTrue(getBinaryDistribution().exists());
@@ -69,27 +71,34 @@ public class BinaryDistributionIT extends AbstractBinaryDistributionTest {
}
@Test
public void runPMD() throws Exception {
String srcDir = new File(".", "src/test/resources/sample-source/java/").getAbsolutePath();
public void testPmdJavaQuickstart() throws Exception {
ExecutionResult result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "rulesets/java/quickstart.xml");
result.assertExecutionResult(4, "");
}
ExecutionResult result;
result = PMDExecutor.runPMD(tempDir); // without any argument, display usage help and error
result.assertExecutionResultErrOutput(1, CliMessages.runWithHelpFlagMessage());
result = PMDExecutor.runPMD(tempDir, "-h");
result.assertExecutionResult(0, SUPPORTED_LANGUAGES_PMD);
result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml");
result.assertExecutionResult(4, "", "JumbledIncrementer.java:8:");
// also test XML format
result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml", "xml");
@Test
public void testPmdXmlFormat() throws Exception {
ExecutionResult result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml", "xml");
result.assertExecutionResult(4, "", "JumbledIncrementer.java\">");
result.assertExecutionResult(4, "", "<violation beginline=\"8\" endline=\"10\" begincolumn=\"13\" endcolumn=\"14\" rule=\"JumbledIncrementer\"");
}
result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "rulesets/java/quickstart.xml");
result.assertExecutionResult(4, "");
@Test
public void testPmdSample() throws Exception {
ExecutionResult result = PMDExecutor.runPMDRules(folder.newFile().toPath(), tempDir, srcDir, "src/test/resources/rulesets/sample-ruleset.xml");
result.assertExecutionResult(4, "", "JumbledIncrementer.java:8:");
}
@Test
public void testPmdHelp() throws Exception {
ExecutionResult result = PMDExecutor.runPMD(tempDir, "-h");
result.assertExecutionResult(0, SUPPORTED_LANGUAGES_PMD);
}
@Test
public void testPmdNoArgs() throws Exception {
ExecutionResult result = PMDExecutor.runPMD(tempDir); // without any argument, display usage help and error
result.assertExecutionResultErrOutput(1, CliMessages.runWithHelpFlagMessage());
}
@Test
@@ -1215,6 +1215,7 @@ fragment UNICODE_LETTER
| [\u1780-\u17B3]
| [\u1820-\u1877]
| [\u1880-\u18A8]
| [\u1D00-\u1DFF]
| [\u1E00-\u1E9B]
| [\u1EA0-\u1EE0]
| [\u1EE1-\u1EF9]
@@ -1264,11 +1265,10 @@ fragment UNICODE_LETTER
| [\u31A0-\u31B7]
| [\u3400]
| [\u4DB5]
| [\u4E00]
| [\u9FA5]
| [\u4E00-\u9EA5]
| [\uA000-\uA48C]
| [\uAC00]
| [\uD7A3]
| [\uA490-\uABFF]
| [\uAC00-\uD7AF]
| [\uF900-\uFA2D]
| [\uFB00-\uFB06]
| [\uFB13-\uFB17]
@@ -40,4 +40,11 @@ public class GoTokenizerTest extends CpdTextComparisonTest {
public void testIssue1751() {
doTest("issue-1751");
}
@Test
public void testUnicode() {
// https://github.com/pmd/pmd/issues/2752
doTest("sample_unicode");
}
}
@@ -0,0 +1,20 @@
func main() {
//string底层是一个byte数组,因此string也可以进行切片操作
str := "hello world"//string是不可变的
slice := str[4:]
fmt.Println(slice)
//若要对string进行修改需要将string修改为byte或rune的切片在操作
//但是转为byte无法进行中文操作
bytes := []byte(str)
bytes[2] = 'x'
str = string(bytes)
fmt.Println(str)
//转换成rune可以对中文进行操作
runes := []rune(str)
runes[0] = '哈'
str = string(runes)
fmt.Println(str)
}
@@ -0,0 +1,89 @@
[Image] or [Truncated image[ Bcol Ecol
L1
[func] 1 5
[main] 6 10
[(] 10 11
[)] 11 12
[{] 13 14
L3
[str] 2 5
[:=] 6 8
["hello world"] 9 22
L4
[slice] 2 7
[:=] 8 10
[str] 11 14
[\[] 14 15
[4] 15 16
[:] 16 17
[\]] 17 18
L5
[fmt] 2 5
[.] 5 6
[Println] 6 13
[(] 13 14
[slice] 14 19
[)] 19 20
L9
[bytes] 2 7
[:=] 8 10
[\[] 11 12
[\]] 12 13
[byte] 13 17
[(] 17 18
[str] 18 21
[)] 21 22
L10
[bytes] 2 7
[\[] 7 8
[2] 8 9
[\]] 9 10
[=] 11 12
['x'] 13 16
L11
[str] 2 5
[=] 6 7
[string] 8 14
[(] 14 15
[bytes] 15 20
[)] 20 21
L12
[fmt] 2 5
[.] 5 6
[Println] 6 13
[(] 13 14
[str] 14 17
[)] 17 18
L15
[runes] 2 7
[:=] 8 10
[\[] 11 12
[\]] 12 13
[rune] 13 17
[(] 17 18
[str] 18 21
[)] 21 22
L16
[runes] 2 7
[\[] 7 8
[0] 8 9
[\]] 9 10
[=] 11 12
['哈'] 13 16
L17
[str] 2 5
[=] 6 7
[string] 8 14
[(] 14 15
[runes] 15 20
[)] 20 21
L18
[fmt] 2 5
[.] 5 6
[Println] 6 13
[(] 13 14
[str] 14 17
[)] 17 18
L20
[}] 1 2
EOF
@@ -18,6 +18,10 @@ import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.Parser;
import net.sourceforge.pmd.lang.ast.Parser.ParserTask;
import net.sourceforge.pmd.lang.ast.SemanticErrorReporter;
import net.sourceforge.pmd.lang.html.ast.ASTHtmlComment;
import net.sourceforge.pmd.lang.html.ast.ASTHtmlDocument;
import net.sourceforge.pmd.lang.html.ast.ASTHtmlTextNode;
import net.sourceforge.pmd.lang.html.ast.HtmlParsingHelper;
import net.sourceforge.pmd.lang.rule.XPathRule;
import net.sourceforge.pmd.lang.rule.xpath.XPathVersion;
@@ -26,25 +30,49 @@ public class HtmlXPathRuleTest {
private static final String LIGHTNING_WEB_COMPONENT = "<!-- helloExpressions.html -->\n"
+ "<template>\n"
+ " <p>Hello, { greeting}!</p>\n"
+ " <lightning-input label=\"Name\" value={ greeting} onchange={handleChange}></lightning-input>"
+ " <lightning-input label=\"Name\" value={ greeting} onchange={handleChange}></lightning-input>\n"
+ " <div class=\"slds-m-around_medium\">\n"
+ " <lightning-input name='firstName' label=\"First Name\" onchange={handleChange}></lightning-input>\n"
+ " <lightning-input name='firstName' label=\"First Name\" onchange={ handleChange}></lightning-input>\n"
+ " <lightning-input name='lastName' label=\"Last Name\" onchange={handleChange}></lightning-input>\n"
+ " <p class=\"slds-m-top_medium\">Uppercased Full Name: {uppercasedFullName}</p>\n"
+ " </div>\n"
+ " <template if:true={visible}>\n"
+ " <p>Test</p>\n"
+ " </template>\n"
+ "</template>";
@Test
public void selectTextNode() {
// from https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.js_props_getter
// "Dont add spaces around the property, for example, { data } is not valid HTML."
String xpath = "//*[local-name() = '#text'][contains(@Text, '{ ')]";
String xpath = "//text()[contains(., '{ ')]";
List<RuleViolation> violations = runXPath(LIGHTNING_WEB_COMPONENT, xpath);
Assert.assertEquals(1, violations.size());
Assert.assertEquals(3, violations.get(0).getBeginLine());
}
@Test
public void selectTextNodeByNodeNameShouldNotWork() {
String xpath = "//*[local-name() = '#text']";
List<RuleViolation> violations = runXPath(LIGHTNING_WEB_COMPONENT, xpath);
Assert.assertEquals(0, violations.size());
}
@Test
public void verifyTextNodeName() {
ASTHtmlDocument document = HtmlParsingHelper.DEFAULT.parse("<p>foobar</p>");
ASTHtmlTextNode textNode = document.getFirstDescendantOfType(ASTHtmlTextNode.class);
Assert.assertEquals("#text", textNode.getXPathNodeName());
}
@Test
public void verifyCommentNodeName() {
ASTHtmlDocument document = HtmlParsingHelper.DEFAULT.parse("<p><!-- a comment --></p>");
ASTHtmlComment comment = document.getFirstDescendantOfType(ASTHtmlComment.class);
Assert.assertEquals("#comment", comment.getXPathNodeName());
}
@Test
public void selectAttributes() {
// from https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.js_props_getter
@@ -56,6 +84,27 @@ public class HtmlXPathRuleTest {
Assert.assertEquals(4, violations.get(0).getBeginLine());
}
@Test
public void selectAttributesMultiple() {
// from https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.js_props_getter
// "Dont add spaces around the property, for example, { data } is not valid HTML."
String xpath = "//*[@*[local-name() = ('value', 'onchange')] = '{']";
List<RuleViolation> violations = runXPath(LIGHTNING_WEB_COMPONENT, xpath);
Assert.assertEquals(2, violations.size());
Assert.assertEquals(4, violations.get(0).getBeginLine());
Assert.assertEquals(6, violations.get(1).getBeginLine());
}
@Test
public void selectAttributeByName() {
String xpath = "//*[@*[local-name() = 'if:true']]";
List<RuleViolation> violations = runXPath(LIGHTNING_WEB_COMPONENT, xpath);
Assert.assertEquals(1, violations.size());
Assert.assertEquals(10, violations.get(0).getBeginLine());
}
private List<RuleViolation> runXPath(String html, String xpath) {
LanguageVersion htmlLanguage = LanguageRegistry.findLanguageByTerseName(HtmlLanguageModule.TERSE_NAME).getDefaultVersion();
Parser parser = htmlLanguage.getLanguageVersionHandler().getParser();
+4 -3
View File
@@ -796,7 +796,7 @@ TOKEN :
"\u1310","\u1312"-"\u1315","\u1318"-"\u131e","\u1320"-"\u1346","\u1348"-"\u135a",
"\u13a0"-"\u13f4","\u1401"-"\u166c","\u166f"-"\u1676","\u1681"-"\u169a","\u16a0"-"\u16ea",
"\u1780"-"\u17b3","\u17db","\u1820"-"\u1877","\u1880"-"\u18a8","\u1e00"-"\u1e9b",
"\u1ea0"-"\u1ef9","\u1f00"-"\u1f15","\u1f18"-"\u1f1d","\u1f20"-"\u1f45","\u1f48"-"\u1f4d",
"\u1ea0"-"\u1ef9","\u1d00"-"\u1eef","\u1f00"-"\u1f15","\u1f18"-"\u1f1d","\u1f20"-"\u1f45","\u1f48"-"\u1f4d",
"\u1f50"-"\u1f57","\u1f59","\u1f5b","\u1f5d","\u1f5f"-"\u1f7d","\u1f80"-"\u1fb4",
"\u1fb6"-"\u1fbc","\u1fbe","\u1fc2"-"\u1fc4","\u1fc6"-"\u1fcc","\u1fd0"-"\u1fd3",
"\u1fd6"-"\u1fdb","\u1fe0"-"\u1fec","\u1ff2"-"\u1ff4","\u1ff6"-"\u1ffc","\u203f"-"\u2040",
@@ -804,7 +804,7 @@ TOKEN :
"\u2124","\u2126","\u2128","\u212a"-"\u212d","\u212f"-"\u2131","\u2133"-"\u2139",
"\u2160"-"\u2183","\u3005"-"\u3007","\u3021"-"\u3029","\u3031"-"\u3035","\u3038"-"\u303a",
"\u3041"-"\u3094","\u309d"-"\u309e","\u30a1"-"\u30fe","\u3105"-"\u312c","\u3131"-"\u318e",
"\u31a0"-"\u31b7","\u3400"-"\u4db5","\u4e00"-"\u9fa5","\ua000"-"\ua48c","\uac00"-"\ud7a3",
"\u31a0"-"\u31b7","\u3400"-"\u4db5","\u4e00"-"\u9fa5","\ua000"-"\ua48c","\ua490"-"\uabff","\uac00"-"\ud7a3",
"\uf900"-"\ufa2d","\ufb00"-"\ufb06","\ufb13"-"\ufb17","\ufb1d","\ufb1f"-"\ufb28",
"\ufb2a"-"\ufb36","\ufb38"-"\ufb3c","\ufb3e","\ufb40"-"\ufb41","\ufb43"-"\ufb44",
"\ufb46"-"\ufbb1","\ufbd3"-"\ufd3d","\ufd50"-"\ufd8f","\ufd92"-"\ufdc7","\ufdf0"-"\ufdfb",
@@ -865,7 +865,7 @@ TOKEN :
"\u12f0"-"\u130e","\u1310","\u1312"-"\u1315","\u1318"-"\u131e","\u1320"-"\u1346",
"\u1348"-"\u135a","\u1369"-"\u1371","\u13a0"-"\u13f4","\u1401"-"\u166c","\u166f"-"\u1676",
"\u1681"-"\u169a","\u16a0"-"\u16ea","\u1780"-"\u17d3","\u17db","\u17e0"-"\u17e9",
"\u180b"-"\u180e","\u1810"-"\u1819","\u1820"-"\u1877","\u1880"-"\u18a9","\u1e00"-"\u1e9b",
"\u180b"-"\u180e","\u1810"-"\u1819","\u1820"-"\u1877","\u1880"-"\u18a9","\u1d00"-"\u1d7f","\u1e00"-"\u1e9b",
"\u1ea0"-"\u1ef9","\u1f00"-"\u1f15","\u1f18"-"\u1f1d","\u1f20"-"\u1f45","\u1f48"-"\u1f4d",
"\u1f50"-"\u1f57","\u1f59","\u1f5b","\u1f5d","\u1f5f"-"\u1f7d","\u1f80"-"\u1fb4",
"\u1fb6"-"\u1fbc","\u1fbe","\u1fc2"-"\u1fc4","\u1fc6"-"\u1fcc","\u1fd0"-"\u1fd3",
@@ -876,6 +876,7 @@ TOKEN :
"\u2160"-"\u2183","\u3005"-"\u3007","\u3021"-"\u302f","\u3031"-"\u3035","\u3038"-"\u303a",
"\u3041"-"\u3094","\u3099"-"\u309a","\u309d"-"\u309e","\u30a1"-"\u30fe","\u3105"-"\u312c",
"\u3131"-"\u318e","\u31a0"-"\u31b7","\u3400"-"\u4db5","\u4e00"-"\u9fa5","\ua000"-"\ua48c",
"\ua490"-"\uabff",
"\uac00"-"\ud7a3","\uf900"-"\ufa2d","\ufb00"-"\ufb06","\ufb13"-"\ufb17","\ufb1d"-"\ufb28",
"\ufb2a"-"\ufb36","\ufb38"-"\ufb3c","\ufb3e","\ufb40"-"\ufb41","\ufb43"-"\ufb44",
"\ufb46"-"\ufbb1","\ufbd3"-"\ufd3d","\ufd50"-"\ufd8f","\ufd92"-"\ufdc7","\ufdf0"-"\ufdfb",
@@ -44,7 +44,13 @@ public class ImmutableFieldRule extends AbstractJavaRulechainRule {
);
private static final Set<String> INVALIDATING_FIELD_ANNOTS =
setOf("lombok.Setter");
setOf(
"lombok.Setter",
"org.mockito.Mock",
"org.mockito.InjectMocks",
"org.springframework.beans.factory.annotation.Autowired",
"org.springframework.boot.test.mock.mockito.MockBean"
);
public ImmutableFieldRule() {
super(ASTFieldDeclaration.class);
@@ -54,7 +60,6 @@ public class ImmutableFieldRule extends AbstractJavaRulechainRule {
@Override
public Object visit(ASTFieldDeclaration field, Object data) {
ASTAnyTypeDeclaration enclosingType = field.getEnclosingType();
if (field.getEffectiveVisibility().isAtMost(Visibility.V_PRIVATE)
&& !field.getModifiers().hasAny(JModifier.VOLATILE, JModifier.STATIC, JModifier.FINAL)
@@ -145,6 +145,12 @@ public class ParserCornersTest extends BaseJavaTreeDumpTest {
java7.parse(code);
}
@Test
public void testUnicodeIndent() {
// https://github.com/pmd/pmd/issues/3423
java7.parseResource("UnicodeIdentifier.java");
}
@Test
public void testParsersCases15() {
doTest("ParserCornerCases", java5);
@@ -0,0 +1,37 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
// https://github.com/pmd/pmd/issues/3423
package com.example.pmdtest;
public class PmdTest {
private static final int lᵤ = 1;
private static final int μᵤ = 2;
public static void main(String[] args) {
System.out.println(lᵤ + μᵤ);
}
}
enum CodeSet {
START_CODE_A('Ë'),
START_CODE_B('Ì'),
START_CODE_C('Í'),
A_TILDE('\u00c3'),
STOP_CODE('Î');
private final char encoding;
CodeSet(final char encoding) {
this.encoding = encoding;
}
public char getEncoding() {
return encoding;
}
}
@@ -516,4 +516,31 @@ public class Foo {
}
]]></code>
</test-code>
<test-code>
<description>With records</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import java.util.Set;
public record Record(Set<String> stringSet) {
public boolean hasMoreThanOneItem() {
return this.stringSet.size() > 1;
}
} ]]></code>
</test-code>
<test-code>
<description>With records and size check</description>
<expected-problems>1</expected-problems>
<expected-linenumbers>6</expected-linenumbers>
<code><![CDATA[
import java.util.Set;
public record Record(Set<String> stringSet) {
public boolean hasMoreThanOneItem() {
return this.stringSet.size() == 0;
}
} ]]></code>
</test-code>
</test-data>
@@ -657,4 +657,35 @@ public class ExampleImmutableField {
}
]]></code>
</test-code>
<test-code>
<description>#3874 [java] ImmutableField reports fields annotated with @Autowired (Spring) and @Mock (Mockito)</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.beans.factory.annotation.Autowired;
public class ExampleImmutableField {
@Mock
private Foo foo;
@InjectMocks
private Bar bar;
@MockBean
private Baz Baz;
@Autowired
private Foo2 foo2;
}
class Foo {}
class Bar {}
class Baz {}
class Foo2 {}
]]></code>
</test-code>
</test-data>

Some files were not shown because too many files have changed in this diff Show More