Merge branch 'master' into pmd/7.0.x

This commit is contained in:
Andreas Dangel 2021-07-31 17:33:53 +02:00
commit aa75b2b5bf
No known key found for this signature in database
GPG Key ID: 93450DF2DF9A3FA3
16 changed files with 279 additions and 11 deletions

View File

@ -79,6 +79,17 @@ The tool comes with a rather extensive help text, simply running with `-help`!
description="Path to file containing a comma delimited list of files to analyze.
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
the given language `<lang>`. Parsing errors are ignored and unparsable files
are skipped.
<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>"
%}
{% include custom/cli_option_row.html options="-ignorelist"
option_arg="filepath"
description="Path to file containing a comma delimited list of files to ignore.
@ -202,3 +213,26 @@ Example:
PMD comes with many different renderers.
All formats are described at [PMD Report formats](pmd_userdocs_report_formats.html)
## Examples
### Analyze other xml formats
If your xml language doesn't use `xml` as file extension, you can still use PMD with `-force-language`:
```
$ ./run.sh pmd -d /home/me/src/xml-file.ext -f text -R ruleset.xml -force-language xml
```
You can also specify a directory instead of a single file. Then all files are analyzed. In that case,
parse errors are suppressed in order to reduce irrelevant noise:
```
$ ./run.sh pmd -d /home/me/src/ -f text -R ruleset.xml -force-language xml
```
Alternatively, you can create a filelist to only analyze files with a given extension:
```
$ find /home/me/src -name "*.ext" > /home/me/src/filelist.txt
$ ./run.sh pmd -filelist /home/me/src/filelist.txt -f text -R ruleset.xml -force-language xml
```

View File

@ -224,9 +224,12 @@ nested element. Possible values are:
<sourceLanguage name="java" version="11"/>
<sourceLanguage name="java" version="12"/>
<sourceLanguage name="java" version="13"/>
<sourceLanguage name="java" version="13-preview"/>
<sourceLanguage name="java" version="14"/> <!-- this is the default -->
<sourceLanguage name="java" version="14-preview"/>
<sourceLanguage name="java" version="14"/>
<sourceLanguage name="java" version="15"/>
<sourceLanguage name="java" version="16"/>
<sourceLanguage name="java" version="16-preview"/>
<sourceLanguage name="java" version="17"/> <!-- this is the default -->
<sourceLanguage name="java" version="17-preview"/>
<sourceLanguage name="jsp" version=""/>
<sourceLanguage name="modelica" version=""/>
<sourceLanguage name="pom" version=""/>

View File

@ -33,6 +33,11 @@ it via the environment variable `PMD_JAVA_OPTS` and select the new language vers
Note: Support for Java 15 preview language features have been removed. The version "15-preview" is no longer available.
#### Updated PMD Designer
This PMD release ships a new version of the pmd-designer.
For the changes, see [PMD Designer Changelog](https://github.com/pmd/pmd-designer/releases/tag/6.37.0).
#### New rules
This release ships with 3 new Java rules.
@ -110,6 +115,7 @@ This release ships with 3 new Java rules.
* [#3329](https://github.com/pmd/pmd/issues/3329): \[apex] ApexCRUDViolation doesn't report SOQL for loops
* core
* [#1603](https://github.com/pmd/pmd/issues/1603): \[core] Language version comparison
* [#2133](https://github.com/pmd/pmd/issues/2133): \[xml] Allow to check Salesforce XML Metadata using XPath rules
* [#3377](https://github.com/pmd/pmd/issues/3377): \[core] NPE when specifying report file in current directory in PMD CLI
* [#3387](https://github.com/pmd/pmd/issues/3387): \[core] CPD should avoid unnecessary copies when running with --skip-lexical-errors
* java-bestpractices
@ -123,6 +129,16 @@ This release ships with 3 new Java rules.
### API Changes
#### PMD CLI
* PMD has a new CLI option `-force-language`. With that a language can be forced 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 the given language. Parsing errors are ignored and unparsable files
are skipped.
This option allows to use the xml language for files, that don't use xml as extension.
See also the examples on [PMD CLI reference](pmd_userdocs_cli_reference.html#analyze-other-xml-formats).
#### Experimental APIs
* The AST types and APIs around Sealed Classes are not experimental anymore:
@ -145,6 +161,7 @@ You can identify them with the `@InternalApi` annotation. You'll also get a depr
* [#3373](https://github.com/pmd/pmd/pull/3373): \[apex] Add ApexCRUDViolation support for database class, inline no-arg object construction DML and inline list initialization DML - [Jonathan Wiesel](https://github.com/jonathanwiesel)
* [#3385](https://github.com/pmd/pmd/pull/3385): \[core] CPD: Optimize --skip-lexical-errors option - [Woongsik Choi](https://github.com/woongsikchoi)
* [#3388](https://github.com/pmd/pmd/pull/3388): \[doc] Add Code Inspector in the list of tools - [Julien Delange](https://github.com/juli1)
* [#3417](https://github.com/pmd/pmd/pull/3417): \[core] Support forcing a specific language from the command-line - [Aidan Harding](https://github.com/aidan-harding)
{% endtocmaker %}

View File

@ -5,6 +5,7 @@
package net.sourceforge.pmd;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
@ -389,7 +390,7 @@ public class PMD {
private static List<DataSource> internalGetApplicableFiles(PMDConfiguration configuration,
Set<Language> languages) {
LanguageFilenameFilter fileSelector = new LanguageFilenameFilter(languages);
FilenameFilter fileSelector = configuration.isForceLanguageVersion() ? new AcceptAllFilenames() : new LanguageFilenameFilter(languages);
List<DataSource> files = new ArrayList<>();
if (null != configuration.getInputPaths()) {
@ -608,4 +609,11 @@ public class PMD {
}
}
private static class AcceptAllFilenames implements FilenameFilter {
@Override
public boolean accept(File dir, String name) {
return true;
}
}
}

View File

@ -92,6 +92,7 @@ public class PMDConfiguration extends AbstractConfiguration {
private int threads = Runtime.getRuntime().availableProcessors();
private ClassLoader classLoader = getClass().getClassLoader();
private LanguageVersionDiscoverer languageVersionDiscoverer = new LanguageVersionDiscoverer();
private LanguageVersion forceLanguageVersion;
// Rule and source file options
private List<String> ruleSets;
@ -214,6 +215,35 @@ public class PMDConfiguration extends AbstractConfiguration {
return languageVersionDiscoverer;
}
/**
* Get the LanguageVersion specified by the force-language parameter. This overrides detection based on file
* extensions
*
* @return The LanguageVersion.
*/
public LanguageVersion getForceLanguageVersion() {
return forceLanguageVersion;
}
/**
* Is the force-language parameter set to anything?
*
* @return true if ${@link #getForceLanguageVersion()} is not null
*/
public boolean isForceLanguageVersion() {
return forceLanguageVersion != null;
}
/**
* Set the LanguageVersion specified by the force-language parameter. This overrides detection based on file
* extensions
*
* @param forceLanguageVersion the language version
*/
public void setForceLanguageVersion(LanguageVersion forceLanguageVersion) {
this.forceLanguageVersion = forceLanguageVersion;
}
/**
* Set the given LanguageVersion as the current default for it's Language.
*

View File

@ -9,6 +9,8 @@ import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.IOUtils;
@ -32,6 +34,8 @@ import net.sourceforge.pmd.lang.ast.SemanticErrorReporter;
@InternalApi
public class SourceCodeProcessor {
private static final Logger LOG = Logger.getLogger(SourceCodeProcessor.class.getName());
private final PMDConfiguration configuration;
private final RulesetStageDependencyHelper dependencyHelper;
@ -114,7 +118,11 @@ public class SourceCodeProcessor {
processSource(sourceCode, ruleSets, ctx);
} catch (ParseException pe) {
configuration.getAnalysisCache().analysisFailed(ctx.getSourceCodeFile());
throw new PMDException("Error while parsing " + ctx.getSourceCodeFile(), pe);
if (configuration.isForceLanguageVersion()) {
LOG.log(Level.FINE, "Error while parsing " + ctx.getSourceCodeFile(), pe);
} else {
throw new PMDException("Error while parsing " + ctx.getSourceCodeFile(), pe);
}
} catch (Exception e) {
configuration.getAnalysisCache().analysisFailed(ctx.getSourceCodeFile());
throw new PMDException("Error while processing " + ctx.getSourceCodeFile(), e);
@ -166,9 +174,18 @@ public class SourceCodeProcessor {
private void determineLanguage(RuleContext ctx) {
// If LanguageVersion of the source file is not known, make a
// determination
if (ctx.getLanguageVersion() == null) {
if (ctx.getLanguageVersion() != null) {
// we already have a language
return;
}
// If LanguageVersion of the source file is not known, make a determination
LanguageVersion forceLanguage = configuration.getForceLanguageVersion();
if (forceLanguage != null) {
// use force language if given
ctx.setLanguageVersion(forceLanguage);
} else {
// otherwise determine by file extension
LanguageVersion languageVersion = configuration.getLanguageVersionOfFile(ctx.getSourceCodeFilename());
ctx.setLanguageVersion(languageVersion);
}

View File

@ -105,6 +105,9 @@ public class PMDParameters {
@Parameter(names = { "-language", "-l" }, description = "Specify a language PMD should use.")
private String language = null;
@Parameter(names = "-force-language", description = "Force a language to be used for all input files, irrespective of filenames.")
private String forceLanguage = null;
@Parameter(names = "-auxclasspath",
description = "Specifies the classpath for libraries used by the source code. "
+ "This is used by the type resolution. The platform specific path delimiter "
@ -218,10 +221,16 @@ public class PMDParameters {
configuration.setAnalysisCacheLocation(this.cacheLocation);
configuration.setIgnoreIncrementalAnalysis(this.isIgnoreIncrementalAnalysis());
LanguageVersion forceLangVersion = getForceLangVersion();
if (forceLangVersion != null) {
configuration.setForceLanguageVersion(forceLangVersion);
}
LanguageVersion languageVersion = getLangVersion();
if (languageVersion != null) {
configuration.getLanguageVersionDiscoverer().setDefaultLanguageVersion(languageVersion);
}
try {
configuration.prependClasspath(this.getAuxclasspath());
} catch (IOException e) {
@ -304,7 +313,7 @@ public class PMDParameters {
return version != null ? lang.getVersion(version)
: lang.getDefaultVersion();
}
public String getVersion() {
if (version != null) {
return version;
@ -316,6 +325,15 @@ public class PMDParameters {
return language != null ? language : LanguageRegistry.getDefaultLanguage().getTerseName();
}
private @Nullable LanguageVersion getForceLangVersion() {
Language lang = forceLanguage != null ? LanguageRegistry.findLanguageByTerseName(forceLanguage) : null;
return lang != null ? lang.getDefaultVersion() : null;
}
public String getForceLanguage() {
return forceLanguage != null ? forceLanguage : "";
}
public String getAuxclasspath() {
return auxclasspath;
}

View File

@ -176,7 +176,7 @@ public final class FileUtil {
/**
* Reads the file, which contains the filelist. This is used for the
* command line arguments --filelist/-filelist for both PMD and CPD.
* The separator in the filelist is a command and/or newlines.
* The separator in the filelist is a comma and/or newlines.
*
* @param filelist the file which contains the list of path names
* @return a comma-separated list of file paths

View File

@ -0,0 +1,26 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang;
import org.junit.Assert;
import org.junit.Test;
import net.sourceforge.pmd.cli.PMDCommandLineInterface;
import net.sourceforge.pmd.cli.PMDParameters;
public class LanguageParameterTest {
/** Test that language parameters from the CLI are correctly passed through to the PMDConfiguration. Although this is a
* CLI test, it resides here to take advantage of {@link net.sourceforge.pmd.lang.DummyLanguageModule}
*/
@Test
public void testLanguageFromCliToConfiguration() {
PMDParameters params = new PMDParameters();
String[] args = { "-d", "source_folder", "-f", "ideaj", "-P", "sourcePath=/home/user/source/", "-R", "java-empty", "-force-language", "dummy"};
PMDCommandLineInterface.extractParameters(params, args, "PMD");
Assert.assertEquals(new DummyLanguageModule().getDefaultVersion().getName(), params.toConfiguration().getForceLanguageVersion().getName());
}
}

View File

@ -0,0 +1,66 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.xml;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.Assert;
import org.junit.Test;
import net.sourceforge.pmd.cli.BaseCLITest;
public class XmlCliTest extends BaseCLITest {
private static final String BASE_DIR = "src/test/resources/net/sourceforge/pmd/lang/xml/cli-tests/sampleproject";
private static final String RULE_MESSAGE = "A tags are not allowed";
private String[] createArgs(String directory, String ... args) {
List<String> arguments = new ArrayList<>();
arguments.add("-f");
arguments.add("text");
arguments.add("-no-cache");
arguments.add("-R");
arguments.add(BASE_DIR + "/ruleset.xml");
arguments.add("-d");
arguments.add(BASE_DIR + directory);
arguments.addAll(Arrays.asList(args));
return arguments.toArray(new String[0]);
}
@Test
public void analyzeSingleXmlWithoutForceLanguage() {
String resultFilename = runTest(createArgs("/src/file1.ext"), "analyzeSingleXmlWithoutForceLanguage", 0);
assertRuleMessage(0, resultFilename);
}
@Test
public void analyzeSingleXmlWithForceLanguage() {
String resultFilename = runTest(createArgs("/src/file1.ext", "-force-language", "xml"),
"analyzeSingleXmlWithForceLanguage", 4);
assertRuleMessage(1, resultFilename);
}
@Test
public void analyzeDirectoryWithForceLanguage() {
String resultFilename = runTest(createArgs("/src/", "-force-language", "xml"),
"analyzeDirectoryWithForceLanguage", 4);
assertRuleMessage(3, resultFilename);
}
private void assertRuleMessage(int expectedCount, String resultFilename) {
try {
String result = FileUtils.readFileToString(new File(resultFilename), StandardCharsets.UTF_8);
Assert.assertEquals(expectedCount, StringUtils.countMatches(result, RULE_MESSAGE));
} catch (IOException e) {
throw new AssertionError(e);
}
}
}

View File

@ -0,0 +1,31 @@
<?xml version="1.0"?>
<ruleset name="sample"
xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
<description>
Sample
</description>
<rule name="A"
language="xml"
message="A tags are not allowed"
class="net.sourceforge.pmd.lang.rule.XPathRule">
<description>
A tags are not allowed
</description>
<priority>3</priority>
<properties>
<property name="version" value="2.0"/>
<property name="xpath">
<value>
<![CDATA[
//a
]]>
</value>
</property>
</properties>
</rule>
</ruleset>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- BSD-style license; for more info see http://pmd.sourceforge.net/license.html -->
<file>
<a></a>
</file>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- BSD-style license; for more info see http://pmd.sourceforge.net/license.html -->
<file>
<a></a>
<a></a>
</file>

View File

@ -0,0 +1,3 @@
BSD-style license; for more info see http://pmd.sourceforge.net/license.html
Other file that is not a xml file.

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- BSD-style license; for more info see http://pmd.sourceforge.net/license.html -->
<file>
</file>

View File

@ -107,7 +107,7 @@
<pmd.build-tools.version>15</pmd.build-tools.version>
<pmd-designer.version>6.27.0</pmd-designer.version>
<pmd-designer.version>6.37.0</pmd-designer.version>
<javacc.jar>${settings.localRepository}/net/java/dev/javacc/javacc/${javacc.version}/javacc-${javacc.version}.jar</javacc.jar>
<javacc.outputDirectory>${project.build.directory}/generated-sources/javacc</javacc.outputDirectory>
<javacc.ant.wrapper>${project.basedir}/../javacc-wrapper.xml</javacc.ant.wrapper>