Merge branch 'master' into pmd/7.0.x

This commit is contained in:
Andreas Dangel
2022-05-28 09:20:01 +02:00
9 changed files with 170 additions and 86 deletions

View File

@ -90,6 +90,8 @@ ruleset. Use the new rule {% rule java/codestyle/UnnecessarySemicolon %} instead
* [#1445](https://github.com/pmd/pmd/issues/1445): \[core] Allow CLI to take globs as parameters
* core
* [#2352](https://github.com/pmd/pmd/issues/2352): \[core] Deprecate \<lang\>-\<ruleset\> hyphen notation for ruleset references
* [#3787](https://github.com/pmd/pmd/issues/3787): \[core] Internalize some methods in Ant Formatter
* [#3835](https://github.com/pmd/pmd/issues/3835): \[core] Deprecate system properties of CPDCommandLineInterface
* [#3942](https://github.com/pmd/pmd/issues/3942): \[core] common-io path traversal vulnerability (CVE-2021-29425)
* cs (c#)
* [#3974](https://github.com/pmd/pmd/pull/3974): \[cs] Add option to ignore C# attributes (annotations)
@ -97,12 +99,15 @@ ruleset. Use the new rule {% rule java/codestyle/UnnecessarySemicolon %} instead
* [#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
* [#3978](https://github.com/pmd/pmd/pull/3978): \[html] Add additional file extensions htm, xhtml, xht, shtml
* 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)
* java-errorprone
* [#3096](https://github.com/pmd/pmd/issues/3096): \[java] EmptyStatementNotInLoop FP in 6.30.0 with IfStatement
* java-performance
* [#3379](https://github.com/pmd/pmd/issues/3379): \[java] UseArraysAsList must ignore primitive arrays
* [#3965](https://github.com/pmd/pmd/issues/3965): \[java] UseArraysAsList false positive with non-trivial loops
@ -132,6 +137,19 @@ Use the explicit forms of these references to be compatible with PMD 7.
{% 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.
#### Internal API
Those APIs are not intended to be used by clients, and will be hidden or removed with PMD 7.0.0.
You can identify them with the `@InternalApi` annotation. You'll also get a deprecation warning.
- {% jdoc core::cpd.CPDCommandLineInterface %} has been internalized. In order to execute CPD either
{% jdoc !!core::cpd.CPD#run(java.lang.String...) %} or {% jdoc !!core::cpd.CPD#main(java.lang.String[]) %}
should be used.
- Several members of {% jdoc test::cli.BaseCPDCLITest %} have been deprecated with replacements.
- The methods {% jdoc !!core::ant.Formatter#start(java.lang.String) %},
{% jdoc !!core::ant.Formatter#end(net.sourceforge.pmd.Report) %}, {% jdoc !!core::ant.Formatter#getRenderer() %},
and {% jdoc !!core::ant.Formatter#isNoOutputSupplied() %} have been internalized.
### External Contributions
* [#3961](https://github.com/pmd/pmd/pull/3961): \[java] Fix #3954 - NPE in UseCollectionIsEmptyRule with record - [@flyhard](https://github.com/flyhard)

View File

@ -65,10 +65,14 @@ public class Formatter {
this.parameters.add(parameter);
}
@Deprecated
@InternalApi
public Renderer getRenderer() {
return renderer;
}
@Deprecated
@InternalApi
public void start(String baseDir) {
Properties properties = createProperties();
@ -115,6 +119,8 @@ public class Formatter {
}
}
@Deprecated
@InternalApi
public void end(Report errorReport) {
try {
renderer.renderFileReport(errorReport);
@ -129,6 +135,8 @@ public class Formatter {
}
}
@Deprecated
@InternalApi
public boolean isNoOutputSupplied() {
return toFile == null && !toConsole;
}
@ -236,6 +244,7 @@ public class Formatter {
return null;
}
@Deprecated
@InternalApi
public GlobalAnalysisListener newListener(Project project, List<String> inputPaths) throws IOException {
start(project.getBaseDir().toString());

View File

@ -4,9 +4,11 @@
package net.sourceforge.pmd.cpd;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@ -19,6 +21,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.cli.internal.CliMessages;
import net.sourceforge.pmd.lang.ast.TokenMgrError;
import net.sourceforge.pmd.util.FileFinder;
import net.sourceforge.pmd.util.IOUtil;
@ -173,7 +176,75 @@ public class CPD {
return new ArrayList<>(source.values());
}
/**
* Entry to invoke CPD as command line tool. Note that this will
* invoke {@link System#exit(int)}.
*
* @param args command line arguments
*/
public static void main(String[] args) {
CPDCommandLineInterface.main(args);
StatusCode statusCode = runCpd(args);
CPDCommandLineInterface.setStatusCodeOrExit(statusCode.toInt());
}
/**
* Parses the command line and executes CPD. Returns the status code
* without exiting the VM.
*
* @param args command line arguments
*
* @return the status code
*/
public static StatusCode runCpd(String... args) {
CPDConfiguration arguments = new CPDConfiguration();
CPD.StatusCode statusCode = CPDCommandLineInterface.parseArgs(arguments, args);
if (statusCode != null) {
return statusCode;
}
CPD cpd = new CPD(arguments);
try {
CPDCommandLineInterface.addSourceFilesToCPD(cpd, arguments);
cpd.go();
if (arguments.getCPDRenderer() == null) {
// legacy writer
System.out.println(arguments.getRenderer().render(cpd.getMatches()));
} else {
arguments.getCPDRenderer().render(cpd.getMatches(), new BufferedWriter(new OutputStreamWriter(System.out)));
}
if (cpd.getMatches().hasNext()) {
if (arguments.isFailOnViolation()) {
statusCode = StatusCode.DUPLICATE_CODE_FOUND;
} else {
statusCode = StatusCode.OK;
}
} else {
statusCode = StatusCode.OK;
}
} catch (IOException | RuntimeException e) {
LOG.debug(e.toString(), e);
LOG.error(CliMessages.errorDetectedMessage(1, CPDCommandLineInterface.PROGRAM_NAME));
statusCode = StatusCode.ERROR;
}
return statusCode;
}
public enum StatusCode {
OK(0),
ERROR(1),
DUPLICATE_CODE_FOUND(4);
private final int code;
StatusCode(int code) {
this.code = code;
}
/** Returns the exit code as used in CLI. */
public int toInt() {
return this.code;
}
}
}

View File

@ -4,11 +4,9 @@
package net.sourceforge.pmd.cpd;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
@ -24,27 +22,45 @@ import org.slf4j.LoggerFactory;
import net.sourceforge.pmd.PMD;
import net.sourceforge.pmd.PMDVersion;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.cli.internal.CliMessages;
import net.sourceforge.pmd.cpd.CPD.StatusCode;
import net.sourceforge.pmd.util.FileUtil;
import net.sourceforge.pmd.util.database.DBURI;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.ParameterException;
/**
* @deprecated Internal API. Use {@link CPD#runCpd(String...)} or {@link CPD#main(String[])}
* in order to execute CPD.
*/
@Deprecated
@InternalApi
public final class CPDCommandLineInterface {
private static final Logger LOG = LoggerFactory.getLogger(CPDCommandLineInterface.class);
private static final int NO_ERRORS_STATUS = 0;
private static final int ERROR_STATUS = 1;
private static final int DUPLICATE_CODE_FOUND = 4;
/**
* @deprecated This is used for testing, but support for it will be removed in PMD 7.
* Use {@link CPD#runCpd(String...)} to avoid exiting the VM. In PMD 7,
* {@link CPD#main(String[])} will call {@link System#exit(int)} always.
*/
@Deprecated
public static final String NO_EXIT_AFTER_RUN = "net.sourceforge.pmd.cli.noExit";
/**
* @deprecated This is used for testing, but support for it will be removed in PMD 7.
* Use {@link CPD#runCpd(String...)} to avoid exiting the VM. In PMD 7,
* {@link CPD#main(String[])} will call {@link System#exit(int)} always.
*/
@Deprecated
public static final String STATUS_CODE_PROPERTY = "net.sourceforge.pmd.cli.status";
private static final String PROGRAM_NAME = "cpd";
static final String PROGRAM_NAME = "cpd";
private CPDCommandLineInterface() { }
@Deprecated
public static void setStatusCodeOrExit(int status) {
if (isExitAfterRunSet()) {
System.exit(status);
@ -65,8 +81,7 @@ public final class CPDCommandLineInterface {
System.setProperty(STATUS_CODE_PROPERTY, Integer.toString(statusCode));
}
public static void main(String[] args) {
CPDConfiguration arguments = new CPDConfiguration();
static StatusCode parseArgs(CPDConfiguration arguments, String... args) {
JCommander jcommander = new JCommander(arguments);
jcommander.setProgramName(PROGRAM_NAME);
@ -75,14 +90,12 @@ public final class CPDCommandLineInterface {
if (arguments.isHelp()) {
jcommander.usage();
System.out.println(buildUsageText());
setStatusCodeOrExit(NO_ERRORS_STATUS);
return;
return StatusCode.OK;
}
} catch (ParameterException e) {
System.err.println(e.getMessage());
System.err.println(CliMessages.runWithHelpFlagMessage());
setStatusCodeOrExit(ERROR_STATUS);
return;
return StatusCode.ERROR;
}
Map<String, String> deprecatedOptions = filterDeprecatedOptions(args);
@ -96,32 +109,16 @@ public final class CPDCommandLineInterface {
// Pass extra parameters as System properties to allow language
// implementation to retrieve their associate values...
CPDConfiguration.setSystemProperties(arguments);
CPD cpd = new CPD(arguments);
try {
addSourceFilesToCPD(cpd, arguments);
return null;
}
cpd.go();
if (arguments.getCPDRenderer() == null) {
// legacy writer
System.out.println(arguments.getRenderer().render(cpd.getMatches()));
} else {
arguments.getCPDRenderer().render(cpd.getMatches(), new BufferedWriter(new OutputStreamWriter(System.out)));
}
if (cpd.getMatches().hasNext()) {
if (arguments.isFailOnViolation()) {
setStatusCodeOrExit(DUPLICATE_CODE_FOUND);
} else {
setStatusCodeOrExit(NO_ERRORS_STATUS);
}
} else {
setStatusCodeOrExit(NO_ERRORS_STATUS);
}
} catch (IOException | RuntimeException e) {
LOG.debug(e.toString(), e);
LOG.error(CliMessages.errorDetectedMessage(1, "CPD"));
setStatusCodeOrExit(ERROR_STATUS);
}
/**
* @deprecated Use {@link CPD#main(String[])}
*/
@Deprecated
public static void main(String[] args) {
setStatusCodeOrExit(CPD.runCpd(args).toInt());
}
private static Map<String, String> filterDeprecatedOptions(String... args) {
@ -212,6 +209,8 @@ public final class CPDCommandLineInterface {
}
}
@Deprecated
@InternalApi
public static String buildUsageText() {
String helpText = " For example on Windows:" + PMD.EOL;

View File

@ -14,20 +14,15 @@ import java.nio.file.Files;
import java.util.Arrays;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.RestoreSystemProperties;
import org.junit.contrib.java.lang.system.SystemErrRule;
import org.junit.contrib.java.lang.system.SystemOutRule;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestRule;
public class CPDCommandLineInterfaceTest {
private static final String SRC_DIR = "src/test/resources/net/sourceforge/pmd/cpd/files/";
@Rule
public final TestRule restoreSystemProperties = new RestoreSystemProperties();
@Rule
public final SystemOutRule log = new SystemOutRule().enableLog().muteForSuccessfulTests();
@Rule
@ -36,15 +31,11 @@ public class CPDCommandLineInterfaceTest {
public TemporaryFolder tempDir = new TemporaryFolder();
@Before
public void setup() {
System.setProperty(CPDCommandLineInterface.NO_EXIT_AFTER_RUN, "true");
}
@Test
public void testEmptyResultRendering() {
CPDCommandLineInterface.main(new String[] { "--minimum-tokens", "340", "--language", "java", "--files",
SRC_DIR, "--format", "xml", });
CPD.StatusCode statusCode = CPD.runCpd("--minimum-tokens", "340", "--language", "java", "--files",
SRC_DIR, "--format", "xml");
Assert.assertEquals(CPD.StatusCode.OK, statusCode);
Assert.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\n" + "<pmd-cpd/>", log.getLog().trim());
}
@ -55,8 +46,9 @@ public class CPDCommandLineInterfaceTest {
new File(SRC_DIR, "dup1.java").getAbsolutePath(),
new File(SRC_DIR, "dup2.java").getAbsolutePath()), StandardCharsets.UTF_8);
CPDCommandLineInterface.main(new String[] { "--minimum-tokens", "340", "--language", "java", "--filelist",
filelist.getAbsolutePath(), "--format", "xml", "-failOnViolation", "true" });
CPD.StatusCode statusCode = CPD.runCpd("--minimum-tokens", "340", "--language", "java", "--filelist",
filelist.getAbsolutePath(), "--format", "xml", "-failOnViolation", "true");
Assert.assertEquals(CPD.StatusCode.OK, statusCode);
Assert.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "\n" + "<pmd-cpd/>", log.getLog().trim());
assertTrue(systemErrRule.getLog().contains("Some deprecated options were used on the command-line, including -failOnViolation"));
assertTrue(systemErrRule.getLog().contains("Consider replacing it with --fail-on-violation"));

View File

@ -13,7 +13,7 @@ public final class HtmlLanguageModule extends BaseLanguageModule {
public static final String TERSE_NAME = "html";
public HtmlLanguageModule() {
super(NAME, null, TERSE_NAME, "html");
super(NAME, null, TERSE_NAME, "html", "htm", "xhtml", "xht", "shtml");
addDefaultVersion("", new HtmlHandler());
}

View File

@ -21,13 +21,10 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
* Test ignore identifiers argument.
*/
@Test
public void testIgnoreIdentifiers() throws Exception {
runCPD("--minimum-tokens", "34", "--language", "java", "--files",
public void testIgnoreIdentifiers() {
String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "34", "--language", "java", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers");
String out = getOutput();
Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication"));
Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
}
/**
@ -35,13 +32,10 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
*/
@Test
public void testIgnoreIdentifiersFailOnViolationFalse() throws Exception {
runCPD("--minimum-tokens", "34", "--language", "java", "--files",
String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "34", "--language", "java", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--failOnViolation",
"false");
String out = getOutput();
Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication"));
Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
}
/**
@ -49,13 +43,10 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
*/
@Test
public void testIgnoreIdentifiersFailOnViolationFalseLongOption() throws Exception {
runCPD("--minimum-tokens", "34", "--language", "java", "--files",
String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "34", "--language", "java", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--fail-on-violation",
"false");
String out = getOutput();
Assert.assertTrue(out.contains("Found a 7 line (36 tokens) duplication"));
Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
}
/**
@ -63,13 +54,10 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
*/
@Test
public void testExcludes() throws Exception {
runCPD("--minimum-tokens", "34", "--language", "java", "--ignore-identifiers", "--files",
String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "34", "--language", "java", "--ignore-identifiers", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--exclude",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/File2.java");
String out = getOutput();
Assert.assertFalse(out.contains("Found a 7 line (34 tokens) duplication"));
Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
}
/**
@ -82,17 +70,15 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
// set the default encoding under Windows
System.setProperty("file.encoding", "Cp1252");
runCPD("--minimum-tokens", "34", "--language", "java", "--files",
String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "34", "--language", "java", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--ignore-identifiers", "--format", "xml",
// request UTF-8 for CPD
"--encoding", "UTF-8");
// reset default encoding
System.setProperty("file.encoding", origEncoding);
String out = getOutput();
Assert.assertTrue(out.startsWith("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"));
Assert.assertTrue(Pattern.compile("System\\.out\\.println\\([ij] \\+ \"ä\"\\);").matcher(out).find());
Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
}
/**
@ -103,30 +89,24 @@ public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
*/
@Test
public void testBrokenAndValidFile() throws IOException {
runCPD("--minimum-tokens", "10", "--language", "java", "--files",
String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "10", "--language", "java", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/badandgood/", "--format", "text", "--skip-lexical-errors");
String out = getOutput();
Assert.assertTrue(
Pattern.compile("Skipping .*?BadFile\\.java\\. Reason: Lexical error in file").matcher(out).find());
Assert.assertTrue(out.contains("Found a 5 line (13 tokens) duplication"));
Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
}
@Test
public void testFormatXmlWithoutEncoding() throws Exception {
runCPD("--minimum-tokens", "10", "--language", "java", "--files",
String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "10", "--language", "java", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/clitest/", "--format", "xml");
String out = getOutput();
Assert.assertTrue(out.contains("<duplication lines=\"3\" tokens=\"10\">"));
Assert.assertEquals(4, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
}
@Test
public void testCSVFormat() throws Exception {
runCPD("--minimum-tokens", "100", "--files", "src/test/resources/net/sourceforge/pmd/cpd/badandgood/",
String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "100", "--files", "src/test/resources/net/sourceforge/pmd/cpd/badandgood/",
"--language", "c", "--format", "csv");
String out = getOutput();
Assert.assertFalse(out.contains("Couldn't instantiate renderer"));
Assert.assertEquals(0, Integer.parseInt(System.getProperty(CPDCommandLineInterface.STATUS_CODE_PROPERTY)));
}
}

View File

@ -15,18 +15,18 @@ import net.sourceforge.pmd.cli.BaseCPDCLITest;
public class CPDCommandLineInterfaceTest extends BaseCPDCLITest {
@Test
public void shouldFindDuplicatesWithDifferentFileExtensions() {
runCPD("--minimum-tokens", "5", "--language", "js", "--files",
String out = runTest(CPD.StatusCode.DUPLICATE_CODE_FOUND, "--minimum-tokens", "5", "--language", "js", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/ts/File1.ts",
"src/test/resources/net/sourceforge/pmd/cpd/ts/File2.ts");
assertThat(getOutput(), containsString("Found a 9 line (32 tokens) duplication in the following files"));
assertThat(out, containsString("Found a 9 line (32 tokens) duplication in the following files"));
}
@Test
public void shouldFindNoDuplicatesWithDifferentFileExtensions() {
runCPD("--minimum-tokens", "5", "--language", "js", "--files",
String out = runTest(CPD.StatusCode.OK, "--minimum-tokens", "5", "--language", "js", "--files",
"src/test/resources/net/sourceforge/pmd/cpd/ts/");
assertThat(getOutput().trim(), emptyString());
assertThat(out.trim(), emptyString());
}
}

View File

@ -9,6 +9,7 @@ import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import net.sourceforge.pmd.cpd.CPD;
@ -34,6 +35,10 @@ public abstract class BaseCPDCLITest {
System.setErr(originalStderr);
}
/**
* @deprecated Use {@link #runTest(CPD.StatusCode, String...)} which returns the output.
*/
@Deprecated
public final String getOutput() {
try {
return bufferStdout.toString("UTF-8");
@ -42,8 +47,18 @@ public abstract class BaseCPDCLITest {
}
}
/**
* @deprecated Use {@link #runTest(CPD.StatusCode, String...)}
*/
@Deprecated
protected void runCPD(String... args) {
System.setProperty(CPDCommandLineInterface.NO_EXIT_AFTER_RUN, "true");
CPD.main(args);
}
protected String runTest(CPD.StatusCode expectedStatusCode, String... args) {
CPD.StatusCode statusCode = CPD.runCpd(args);
Assert.assertEquals("Unexpected status code", expectedStatusCode, statusCode);
return getOutput();
}
}