From ab0d740d81b371bff661c519efa43ec577e405bc Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 1 Mar 2024 11:38:04 +0100 Subject: [PATCH 1/2] Cleanup some TODOs --- .../net/sourceforge/pmd/ant/Formatter.java | 1 - .../net/sourceforge/pmd/ant/CPDTaskTest.java | 20 +++++++++--- .../net/sourceforge/pmd/ant/src/sample2.dummy | 1 + .../sourceforge/pmd/ant/xml/cpdtasktest.xml | 6 ++-- .../pmd/ant/xml/expected-pmd-ant-xml.xml | 8 +++++ .../pmd/lang/apex/ApexLanguageProperties.java | 6 ++-- .../pmd/lang/apex/ast/ApexQualifiedName.java | 17 ++++++++-- .../apex/multifile/ApexMultifileAnalysis.java | 9 +++--- .../lang/apex/ast/ApexQualifiedNameTest.java | 22 +++++++++++++ .../multifile/ApexMultifileAnalysisTest.java | 7 ++--- .../apex/rule/design/UnusedMethodTest.java | 3 +- .../pmd/cli/internal/PmdRootLogger.java | 1 - .../java/net/sourceforge/pmd/PmdAnalysis.java | 2 +- .../pmd/lang/JvmLanguagePropertyBundle.java | 1 - .../pmd/lang/LanguageProcessorRegistry.java | 2 +- .../pmd/lang/LanguagePropertyBundle.java | 1 + .../sourceforge/pmd/lang/ast/NodeStream.java | 2 +- .../pmd/lang/document/FileLocation.java | 4 --- .../pmd/lang/document/TextFileContent.java | 6 +++- .../sourceforge/pmd/lang/rule/RuleSet.java | 2 +- .../properties/AbstractPropertySource.java | 6 +--- .../pmd/renderers/IDEAJRenderer.java | 1 - .../pmd/renderers/JsonRenderer.java | 4 --- .../pmd/renderers/XMLRenderer.java | 1 - .../pmd/renderers/XSLTRenderer.java | 7 +++-- .../pmd/renderers/YAHTMLRenderer.java | 1 - .../sourceforge/pmd/reporting/Reportable.java | 3 +- .../ast/ASTCompactConstructorDeclaration.java | 4 +-- .../lang/ecmascript/ast/ASTSwitchCase.java | 1 - .../lang/ecmascript/ast/EcmascriptParser.java | 31 ++++++++++++++++--- .../ecmascript/ast/EcmascriptParserTest.java | 23 ++++++++++++-- .../lang/kotlin/ast/KotlinNameDictionary.java | 6 ---- .../pmd/lang/scala/ast/ScalaNode.java | 1 - .../pmd/test/schema/BaseTestParserImpl.java | 21 ++++++++----- .../lang/vf/ast/VfExpressionTypeVisitor.java | 1 - 35 files changed, 155 insertions(+), 77 deletions(-) create mode 100644 pmd-ant/src/test/resources/net/sourceforge/pmd/ant/src/sample2.dummy diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/Formatter.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/Formatter.java index cd36777ca9..5034fa0d9b 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/Formatter.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/Formatter.java @@ -150,7 +150,6 @@ public class Formatter { return sb.toString(); } - // FIXME - hm, what about this consoleRenderer thing... need a test for this Renderer createRenderer() { if (StringUtils.isBlank(type)) { throw new BuildException(unknownRendererMessage("")); diff --git a/pmd-ant/src/test/java/net/sourceforge/pmd/ant/CPDTaskTest.java b/pmd-ant/src/test/java/net/sourceforge/pmd/ant/CPDTaskTest.java index 5be00096c5..9bb924312d 100644 --- a/pmd-ant/src/test/java/net/sourceforge/pmd/ant/CPDTaskTest.java +++ b/pmd-ant/src/test/java/net/sourceforge/pmd/ant/CPDTaskTest.java @@ -4,13 +4,22 @@ package net.sourceforge.pmd.ant; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import net.sourceforge.pmd.internal.util.IOUtil; + /** * * @author Romain Pelisse <belaran@gmail.com> @@ -24,10 +33,13 @@ class CPDTaskTest extends AbstractAntTest { } @Test - void testBasic() { + void testBasic() throws IOException { executeTarget("testBasic"); - // FIXME: This clearly needs to be improved - but I don't like to write - // test, so feel free to contribute :) - assertTrue(new File("target/cpd.ant.tests").exists()); + Path report = Paths.get("target/cpd.ant.tests"); + assertTrue(Files.exists(report), "Report was not created"); + String reportContent = IOUtil.readFileToString(report.toFile(), StandardCharsets.UTF_8); + assertThat(reportContent, containsString("Found a 1 line (21 tokens) duplication in the following files:")); + assertThat(reportContent, containsString("sample.dummy")); + assertThat(reportContent, containsString("sample2.dummy")); } } diff --git a/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/src/sample2.dummy b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/src/sample2.dummy new file mode 100644 index 0000000000..b84ebcb982 --- /dev/null +++ b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/src/sample2.dummy @@ -0,0 +1 @@ +Content does not matter as long as the extension is "dummy", so that the DummyLanguageModule/Parser is used. diff --git a/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml index 3f84018714..df4f993f92 100644 --- a/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml +++ b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/cpdtasktest.xml @@ -1,7 +1,7 @@ - + @@ -9,8 +9,8 @@ - - + + diff --git a/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/expected-pmd-ant-xml.xml b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/expected-pmd-ant-xml.xml index 6e7f517c67..6045610122 100644 --- a/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/expected-pmd-ant-xml.xml +++ b/pmd-ant/src/test/resources/net/sourceforge/pmd/ant/xml/expected-pmd-ant-xml.xml @@ -8,4 +8,12 @@ Test Rule 2 Test Rule 3 + + +Test Rule 2 + + +Test Rule 3 + + diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageProperties.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageProperties.java index 1b33565565..0ff84da9dc 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageProperties.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ApexLanguageProperties.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.apex; +import java.util.Optional; + import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertyFactory; @@ -13,11 +15,11 @@ import net.sourceforge.pmd.properties.PropertyFactory; */ public class ApexLanguageProperties extends LanguagePropertyBundle { - // todo change that to optional when properties are updated - public static final PropertyDescriptor MULTIFILE_DIRECTORY = + public static final PropertyDescriptor> MULTIFILE_DIRECTORY = PropertyFactory.stringProperty("rootDirectory") .desc("The root directory of the Salesforce metadata, where `sfdx-project.json` resides.") .defaultValue("") // is this ok? + .toOptional("") .build(); public ApexLanguageProperties() { diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedName.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedName.java index b23bd36032..00ea17431f 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedName.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedName.java @@ -4,9 +4,12 @@ package net.sourceforge.pmd.lang.apex.ast; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -16,6 +19,7 @@ import java.util.stream.Collectors; * @author Clément Fournier */ public final class ApexQualifiedName { + private static final Pattern QUALIFIED_NAME_PATTERN = Pattern.compile("(?\\w+)(?:.(?\\w+))?(?:#(?\\w+\\(.*\\)))?"); private final String[] classes; private final String operation; @@ -105,7 +109,6 @@ public final class ApexQualifiedName { } - /** * Parses a string conforming to the format defined below and returns an ApexQualifiedName. * @@ -119,9 +122,17 @@ public final class ApexQualifiedName { * * @return An ApexQualifiedName, or null if the string couldn't be parsed */ - // private static final Pattern FORMAT = Pattern.compile("(\\w+)(.(\\w+))?(#(\\w+))?"); // TODO public static ApexQualifiedName ofString(String toParse) { - throw new UnsupportedOperationException(); + Matcher matcher = QUALIFIED_NAME_PATTERN.matcher(toParse); + if (matcher.matches()) { + List classNames = new ArrayList<>(); + classNames.add(matcher.group("class1")); + if (matcher.group("class2") != null) { + classNames.add(matcher.group("class2")); + } + return new ApexQualifiedName(classNames.toArray(new String[0]), matcher.group("operation")); + } + return null; } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/multifile/ApexMultifileAnalysis.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/multifile/ApexMultifileAnalysis.java index ba9bbe228c..85b6429bda 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/multifile/ApexMultifileAnalysis.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/multifile/ApexMultifileAnalysis.java @@ -10,6 +10,7 @@ import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Optional; import org.checkerframework.checker.nullness.qual.Nullable; import org.slf4j.Logger; @@ -52,20 +53,20 @@ public final class ApexMultifileAnalysis { ApexMultifileAnalysis(ApexLanguageProperties properties) { - String rootDir = properties.getProperty(ApexLanguageProperties.MULTIFILE_DIRECTORY); + Optional rootDir = properties.getProperty(ApexLanguageProperties.MULTIFILE_DIRECTORY); LOG.debug("MultiFile Analysis created for {}", rootDir); Org org = null; try { // Load the package into the org, this can take some time! - if (rootDir != null && !rootDir.isEmpty()) { - Path projectPath = Paths.get(rootDir); + if (rootDir.isPresent() && !rootDir.get().isEmpty()) { + Path projectPath = Paths.get(rootDir.get()); Path sfdxProjectJson = projectPath.resolve("sfdx-project.json"); // Limit analysis to SFDX Projects // MDAPI analysis is currently supported but is expected to be deprecated soon if (Files.isDirectory(projectPath) && Files.isRegularFile(sfdxProjectJson)) { - org = Org.newOrg(rootDir); + org = Org.newOrg(rootDir.get()); // FIXME: Syntax & Semantic errors found during Org loading are not currently being reported. These // should be routed to the new SemanticErrorReporter but that is not available for use just yet. diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedNameTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedNameTest.java index c8ee269627..bfe0af7510 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedNameTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexQualifiedNameTest.java @@ -4,9 +4,11 @@ package net.sourceforge.pmd.lang.apex.ast; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; @@ -114,4 +116,24 @@ class ApexQualifiedNameTest extends ApexParserTestBase { assertTrue(m.getQualifiedName().toString().startsWith("Outer.Inner#")); } } + + @Test + void testOfString() { + assertQualifiedName(new String[] { "MyClass" }, true, null, ApexQualifiedName.ofString("MyClass")); + assertQualifiedName(new String[] { "Outer", "MyClass" }, true, null, ApexQualifiedName.ofString("Outer.MyClass")); + assertQualifiedName(new String[] { "Foo" }, false, "foo(String, Foo)", ApexQualifiedName.ofString("Foo#foo(String, Foo)")); + } + + private static void assertQualifiedName(String[] expectedClasses, boolean isClass, String expectedOperation, ApexQualifiedName name) { + assertArrayEquals(expectedClasses, name.getClasses()); + assertEquals(isClass, name.isClass()); + assertEquals(!isClass, name.isOperation()); + assertEquals(expectedOperation, name.getOperation()); + + if (isClass) { + assertSame(name, name.getClassName()); + } else { + assertArrayEquals(expectedClasses, name.getClassName().getClasses()); + } + } } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/multifile/ApexMultifileAnalysisTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/multifile/ApexMultifileAnalysisTest.java index a230bfe7e4..4f8a22df34 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/multifile/ApexMultifileAnalysisTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/multifile/ApexMultifileAnalysisTest.java @@ -14,6 +14,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; +import java.util.Optional; import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.jupiter.api.Test; @@ -64,16 +65,12 @@ class ApexMultifileAnalysisTest { assertFalse(analysisInstance.isFailed()); }); - // TODO: log is not empty due to ANTLR versions, 4.9.1 vs 4.8, expect to resolve with apex-dev-tools switch - log = log.replace("ANTLR Tool version 4.8 used for code generation does not match the current runtime version 4.9.1", ""); - log = log.replace("ANTLR Runtime version 4.8 used for parser compilation does not match the current runtime version 4.9.1", ""); - log = log.trim(); assertTrue(log.isEmpty()); } private @NonNull ApexMultifileAnalysis getAnalysisForTempFolder() { ApexLanguageProperties props = new ApexLanguageProperties(); - props.setProperty(ApexLanguageProperties.MULTIFILE_DIRECTORY, tempFolder.toAbsolutePath().toString()); + props.setProperty(ApexLanguageProperties.MULTIFILE_DIRECTORY, Optional.of(tempFolder.toAbsolutePath().toString())); return new ApexMultifileAnalysis(props); } diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/design/UnusedMethodTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/design/UnusedMethodTest.java index 937199a36f..27f971dc74 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/design/UnusedMethodTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/rule/design/UnusedMethodTest.java @@ -9,6 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Optional; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; @@ -61,7 +62,7 @@ class UnusedMethodTest { configuration.setThreads(0); // don't use separate threads configuration.prependAuxClasspath("."); - configuration.getLanguageProperties(apexLanguage).setProperty(ApexLanguageProperties.MULTIFILE_DIRECTORY, testProjectDir.toString()); + configuration.getLanguageProperties(apexLanguage).setProperty(ApexLanguageProperties.MULTIFILE_DIRECTORY, Optional.of(testProjectDir.toString())); RuleSet parsedRset = new RuleSetLoader().warnDeprecated(false).loadFromResource("category/apex/design.xml"); Rule rule = parsedRset.getRuleByName("UnusedMethod"); diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/internal/PmdRootLogger.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/internal/PmdRootLogger.java index 54f23ad079..934dbc7e2f 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/internal/PmdRootLogger.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/internal/PmdRootLogger.java @@ -64,7 +64,6 @@ public final class PmdRootLogger { // In pmd-cli, we use slf4j-simple. // create a top-level reporter - // TODO CLI errors should also be reported through this PmdReporter pmdReporter = new SimpleMessageReporter(log); // always install java.util.logging to slf4j bridge Slf4jSimpleConfiguration.installJulBridge(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java index fbfd9eb6f2..59454ff84d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java @@ -211,7 +211,7 @@ public final class PmdAnalysis implements AutoCloseable { } // TODO replace those with actual language properties when the - // CLI syntax is implemented. + // CLI syntax is implemented. #2947 props.setProperty(LanguagePropertyBundle.SUPPRESS_MARKER, config.getSuppressMarker()); if (props instanceof JvmLanguagePropertyBundle) { ((JvmLanguagePropertyBundle) props).setClassLoader(config.getClassLoader()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/JvmLanguagePropertyBundle.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/JvmLanguagePropertyBundle.java index ef3f5f142c..25ccb2e719 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/JvmLanguagePropertyBundle.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/JvmLanguagePropertyBundle.java @@ -22,7 +22,6 @@ import net.sourceforge.pmd.properties.PropertyFactory; */ public class JvmLanguagePropertyBundle extends LanguagePropertyBundle { - // TODO make that a PropertyDescriptor public static final PropertyDescriptor AUX_CLASSPATH = PropertyFactory.stringProperty("auxClasspath") .desc("A classpath to use to resolve references to external types in the analysed sources. " diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessorRegistry.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessorRegistry.java index dd11a890e7..eb9fd9a5c5 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessorRegistry.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageProcessorRegistry.java @@ -149,7 +149,7 @@ public final class LanguageProcessorRegistry implements AutoCloseable { return new LanguageProcessorRegistry(processors); } - // TODO this should be reused when implementing the CLI + // TODO this should be reused when implementing the CLI - see https://github.com/pmd/pmd/issues/2947 public static Map derivePropertiesFromStrings( Map stringProperties, PmdReporter reporter diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguagePropertyBundle.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguagePropertyBundle.java index 5761f275c5..bb39b83646 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguagePropertyBundle.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguagePropertyBundle.java @@ -23,6 +23,7 @@ public class LanguagePropertyBundle extends AbstractPropertySource { // todo for now i think an empty value might interpret every comment // as a suppression. I think it should disable suppression comments. + // #4846 public static final PropertyDescriptor SUPPRESS_MARKER = PropertyFactory.stringProperty("suppressMarker") .desc("Marker to identify suppression comments. " diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/NodeStream.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/NodeStream.java index 1e7d7c1b10..cdf84cd49f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/NodeStream.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/ast/NodeStream.java @@ -105,7 +105,7 @@ import net.sourceforge.pmd.lang.ast.internal.StreamImpl; * equivalent to {@link Stream#findAny()}. The method {@link #first()} * is an equivalent to {@link Stream#findFirst()}. There is however a * {@link #last()} method, which may be implemented efficiently on some - * streams (eg {@link #children()}). TODO maybe implement reverse + * streams (eg {@link #children()}). * *

Node streams are most of the time ordered in document order (w.r.t. the XPath specification), * a.k.a. prefix order. Some operations which explicitly manipulate the order of nodes, like diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileLocation.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileLocation.java index e9a6d89ab2..e860c9f5b0 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileLocation.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/FileLocation.java @@ -22,10 +22,6 @@ import net.sourceforge.pmd.util.AssertionUtil; * *

This should replace the text coordinates methods in {@link Node}, * {@link GenericToken}, and {@link RuleViolation} at least (see {@link Reportable}). - * - * TODO the end line/end column are barely used, mostly ignored even by - * renderers. Maybe these could be optional, or replaced by just a length - * in case a renderer wants to cut out a piece of the file. */ public final class FileLocation { diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFileContent.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFileContent.java index b973f91d89..704ff9aa09 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFileContent.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/TextFileContent.java @@ -22,6 +22,8 @@ import java.util.zip.Checksum; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import net.sourceforge.pmd.internal.util.IOUtil; @@ -29,6 +31,7 @@ import net.sourceforge.pmd.internal.util.IOUtil; * Contents of a text file. */ public final class TextFileContent { + private static final Logger LOGGER = LoggerFactory.getLogger(TextFileContent.class); // the three line terminators we handle. private static final String CRLF = "\r\n"; @@ -298,7 +301,8 @@ public final class TextFileContent { if (curLineTerm.equals(newLineTerm)) { return curLineTerm; } else { - // todo maybe we should report a warning + // todo maybe we should report a warning with filename + LOGGER.debug("Detect mixed line terminators. Falling back to system default."); return fallback; // mixed line terminators, fallback to system default } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSet.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSet.java index c4a5d5b60a..7d39f31c1d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSet.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/rule/RuleSet.java @@ -67,7 +67,7 @@ public class RuleSet implements ChecksumAware { fileName = builder.fileName; name = Objects.requireNonNull(builder.name, MISSING_RULESET_NAME); description = Objects.requireNonNull(builder.description, MISSING_RULESET_DESCRIPTION); - // TODO: ideally, the rules would be unmodifiable, too. But removeDysfunctionalRules might change the rules. + // TODO: ideally, the rules would be unmodifiable, too. But removeDysfunctionalRules might change the rules. #3868 rules = builder.rules; excludePatterns = Collections.unmodifiableList(new ArrayList<>(builder.excludePatterns)); includePatterns = Collections.unmodifiableList(new ArrayList<>(builder.includePatterns)); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java index 3ecbfb7070..5b4939b29f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/properties/AbstractPropertySource.java @@ -19,13 +19,9 @@ import java.util.Objects; */ public abstract class AbstractPropertySource implements PropertySource { - // setProperty should probably be hidden from Rule implementations, they have no business using that + // TODO setProperty should probably be hidden from Rule implementations, they have no business using that // The apex rules that do that could be refactored to do it in the XML - // TODO RuleReference should extend this class - // This would avoid duplicating the implementation between Rule and RuleReference, - // which should use exactly the same mechanism to override properties (XML). - /** * The list of known properties that can be configured. */ diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/IDEAJRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/IDEAJRenderer.java index 9dd2b7501e..fcfa274fde 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/IDEAJRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/IDEAJRenderer.java @@ -25,7 +25,6 @@ public class IDEAJRenderer extends AbstractIncrementingRenderer { public static final String NAME = "ideaj"; - // TODO 7.0.0 use PropertyDescriptor public static final PropertyDescriptor FILE_NAME = PropertyFactory.stringProperty("fileName").desc("File name.").defaultValue("").build(); public static final PropertyDescriptor SOURCE_PATH = diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/JsonRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/JsonRenderer.java index 11ff613259..388dca779f 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/JsonRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/JsonRenderer.java @@ -24,10 +24,6 @@ import com.google.gson.stream.JsonWriter; public class JsonRenderer extends AbstractIncrementingRenderer { public static final String NAME = "json"; - // TODO do we make this public? It would make it possible to write eg - // if (jsonObject.getInt("formatVersion") > JsonRenderer.FORMAT_VERSION) - // /* handle unsupported version */ - // because the JsonRenderer.FORMAT_VERSION would be hardcoded by the compiler private static final int FORMAT_VERSION = 0; private static final Map SUPPRESSION_TYPE_FORMAT_0 = new HashMap<>(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java index 7699d26c6c..bddc59b842 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XMLRenderer.java @@ -39,7 +39,6 @@ public class XMLRenderer extends AbstractIncrementingRenderer { public static final String NAME = "xml"; - // TODO 7.0.0 use PropertyDescriptor or something more specialized public static final PropertyDescriptor ENCODING = PropertyFactory.stringProperty("encoding").desc("XML encoding format").defaultValue("UTF-8").build(); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XSLTRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XSLTRenderer.java index de3cc8ef65..d505eb08eb 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XSLTRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/XSLTRenderer.java @@ -41,8 +41,11 @@ public class XSLTRenderer extends XMLRenderer { public static final String NAME = "xslt"; - // TODO 7.0.0 use PropertyDescriptor> - public static final PropertyDescriptor XSLT_FILENAME = PropertyFactory.stringProperty("xsltFilename").desc("The XSLT file name.").defaultValue("").build(); + public static final PropertyDescriptor XSLT_FILENAME = PropertyFactory + .stringProperty("xsltFilename") + .desc("The XSLT file name.") + .defaultValue("") + .build(); private Transformer transformer; private String xsltFilename = "/pmd-nicerhtml.xsl"; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/YAHTMLRenderer.java b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/YAHTMLRenderer.java index 9001f5a05c..03170d23ae 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/renderers/YAHTMLRenderer.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/renderers/YAHTMLRenderer.java @@ -28,7 +28,6 @@ import net.sourceforge.pmd.util.StringUtil; public class YAHTMLRenderer extends AbstractAccumulatingRenderer { public static final String NAME = "yahtml"; - // TODO 7.0.0 use PropertyDescriptor> with a constraint that the file is an existing directory public static final PropertyDescriptor OUTPUT_DIR = PropertyFactory.stringProperty("outputDir") .desc("Output directory.") diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/reporting/Reportable.java b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/Reportable.java index d159e55d85..0218147b01 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/reporting/Reportable.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/reporting/Reportable.java @@ -12,9 +12,8 @@ import net.sourceforge.pmd.lang.document.FileLocation; * Interface implemented by those objects that can be the target of * a {@link RuleViolation}. {@link Node}s and {@link GenericToken tokens} * implement this interface. - * - * TODO use this in RuleViolationFactory */ +// TODO use this in RuleContext where RuleViolations are created public interface Reportable { // todo add optional method to get the nearest node, to implement diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCompactConstructorDeclaration.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCompactConstructorDeclaration.java index 04d2bf3677..c7e86c6464 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCompactConstructorDeclaration.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTCompactConstructorDeclaration.java @@ -14,8 +14,6 @@ import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol; * *

Compact record constructors must be declared "public". * - * TODO make implicit formal parameter node and implement ASTExecutableDeclaration. - * *

  *
  * CompactConstructorDeclaration ::=  {@link ASTModifierList Modifiers}
@@ -24,6 +22,8 @@ import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
  *
  * 
*/ +// TODO make implicit formal parameter node and implement ASTExecutableDeclaration. +// This might help UnusedAssignmentRule / DataflowPass.ReachingDefsVisitor, see also #4603 public final class ASTCompactConstructorDeclaration extends AbstractJavaNode implements ASTBodyDeclaration, SymbolDeclaratorNode, ModifierOwner { ASTCompactConstructorDeclaration(int id) { diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/ASTSwitchCase.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/ASTSwitchCase.java index 18a6a9d5ce..fc77666c46 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/ASTSwitchCase.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/ASTSwitchCase.java @@ -29,7 +29,6 @@ public final class ASTSwitchCase extends AbstractEcmascriptNode { } public int getNumStatements() { - // TODO Tell Rhino folks about null Statements, should be empty List? return node.getStatements() != null ? node.getStatements().size() : 0; } diff --git a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParser.java b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParser.java index 6f6c00b5f8..6d2627eb92 100644 --- a/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParser.java +++ b/pmd-javascript/src/main/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParser.java @@ -8,6 +8,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Predicate; +import java.util.stream.Collectors; import org.mozilla.javascript.CompilerEnvirons; import org.mozilla.javascript.Context; @@ -16,6 +18,8 @@ import org.mozilla.javascript.ast.AstRoot; import org.mozilla.javascript.ast.Comment; import org.mozilla.javascript.ast.ErrorCollector; import org.mozilla.javascript.ast.ParseProblem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import net.sourceforge.pmd.lang.LanguagePropertyBundle; import net.sourceforge.pmd.lang.LanguageVersion; @@ -23,15 +27,19 @@ import net.sourceforge.pmd.lang.ast.AstInfo; import net.sourceforge.pmd.lang.ast.FileAnalysisException; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.RootNode; +import net.sourceforge.pmd.lang.document.FileId; +import net.sourceforge.pmd.lang.document.FileLocation; +import net.sourceforge.pmd.lang.document.TextPos2d; public final class EcmascriptParser implements net.sourceforge.pmd.lang.ast.Parser { + private static final Logger LOGGER = LoggerFactory.getLogger(EcmascriptParser.class); private final LanguagePropertyBundle properties; public EcmascriptParser(LanguagePropertyBundle properties) { this.properties = properties; } - private AstRoot parseEcmascript(final String sourceCode, final LanguageVersion version, final List parseProblems) throws ParseException { + private AstRoot parseEcmascript(final FileId fileId, final String sourceCode, final LanguageVersion version, final List parseProblems) throws ParseException { final CompilerEnvirons compilerEnvirons = new CompilerEnvirons(); compilerEnvirons.setRecordingComments(true); compilerEnvirons.setRecordingLocalJsDocComments(true); @@ -42,11 +50,9 @@ public final class EcmascriptParser implements net.sourceforge.pmd.lang.ast.Pars // see bug #1150 "EmptyExpression" for valid statements! compilerEnvirons.setReservedKeywordAsIdentifier(true); - // TODO We should do something with Rhino errors... final ErrorCollector errorCollector = new ErrorCollector(); final Parser parser = new Parser(compilerEnvirons, errorCollector); - // TODO Fix hardcode - final String sourceURI = "unknown"; + final String sourceURI = fileId.getOriginalPath(); final int beginLineno = 1; AstRoot astRoot = parser.parse(sourceCode, sourceURI, beginLineno); parseProblems.addAll(errorCollector.getErrors()); @@ -65,7 +71,22 @@ public final class EcmascriptParser implements net.sourceforge.pmd.lang.ast.Pars public RootNode parse(ParserTask task) throws FileAnalysisException { final LanguageVersion version = task.getLanguageVersion(); final List parseProblems = new ArrayList<>(); - final AstRoot astRoot = parseEcmascript(task.getSourceText(), version, parseProblems); + final AstRoot astRoot = parseEcmascript(task.getFileId(), task.getSourceText(), version, parseProblems); + + List errors = parseProblems.stream().filter(p -> p.getType() == ParseProblem.Type.Error).collect(Collectors.toList()); + if (!errors.isEmpty()) { + String errorMessage = errors.stream().map(p -> { + TextPos2d textPos2d = task.getTextDocument().lineColumnAtOffset(p.getFileOffset()); + FileLocation caret = FileLocation.caret(task.getFileId(), textPos2d.getLine(), textPos2d.getColumn()); + return caret.startPosToStringWithFile() + ": " + p.getMessage(); + }).collect(Collectors.joining(System.lineSeparator())); + + // TODO throw new ParseException(errors.size() + " problems found:" + System.lineSeparator() + errorMessage); + // can't throw ParseException as that would fail many analysis. The parser replaced the errors with + // EmptyStatement. + LOGGER.warn("{} javascript problems found:{}{}", errors.size(), System.lineSeparator(), errorMessage); + } + final EcmascriptTreeBuilder treeBuilder = new EcmascriptTreeBuilder(parseProblems); ASTAstRoot tree = (ASTAstRoot) treeBuilder.build(astRoot); diff --git a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java index 34556caa54..ee5354a7ee 100644 --- a/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java +++ b/pmd-javascript/src/test/java/net/sourceforge/pmd/lang/ecmascript/ast/EcmascriptParserTest.java @@ -173,13 +173,30 @@ class EcmascriptParserTest extends EcmascriptParserTestBase { */ @Test void testXorAssignment() { - ASTAstRoot rootNode = js.parse("function f() { var x = 2; x ^= 2; x &= 2; x |= 2; " - + "x &&= true; x ||= false; x *= 2; x /= 2; x %= 2; x += 2; x -= 2; " - + "x <<= 2; x >>= 2; x >>>= 2; }"); + ASTAstRoot rootNode = js.parse( + "function f() {\n" + + " var x = 2;\n" + + " x ^= 2;\n" + + " x &= 2;\n" + + " x |= 2;\n" + + " x &&= true;\n" + + " x ||= false;\n" + + " x *= 2;\n" + + " x /= 2;\n" + + " x %= 2;\n" + + " x += 2;\n" + + " x -= 2;\n" + + " x <<= 2;\n" + + " x >>= 2;\n" + + " x >>>= 2;\n" + + "}"); ASTAssignment infix = rootNode.descendants(ASTAssignment.class).first(); assertEquals("^=", infix.getOperator()); } + /** + * See [js] Support unicode characters #2605 + */ @Test void testUnicodeCjk() { // the first is u+4F60 diff --git a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinNameDictionary.java b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinNameDictionary.java index 387717bb53..d1a0a1313e 100644 --- a/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinNameDictionary.java +++ b/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/ast/KotlinNameDictionary.java @@ -15,10 +15,4 @@ final class KotlinNameDictionary extends AntlrNameDictionary { KotlinNameDictionary(Vocabulary vocab, String[] ruleNames) { super(vocab, ruleNames); } - - @Override - protected @Nullable String nonAlphaNumName(String name) { - // todo - return super.nonAlphaNumName(name); - } } diff --git a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ast/ScalaNode.java b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ast/ScalaNode.java index ab5261b785..8cfdccd5fb 100644 --- a/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ast/ScalaNode.java +++ b/pmd-scala-modules/pmd-scala-common/src/main/java/net/sourceforge/pmd/lang/scala/ast/ScalaNode.java @@ -25,6 +25,5 @@ public interface ScalaNode extends GenericNode> { */ // TODO this would be useful on the node interface for 7.0.0. // we could filter them out from violations transparently - // Apex has the same problem boolean isImplicit(); } diff --git a/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java b/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java index 5730abbdac..0f26b41bf2 100644 --- a/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java +++ b/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java @@ -23,10 +23,12 @@ import org.w3c.dom.Node; import net.sourceforge.pmd.lang.Language; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.rule.Rule; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.test.schema.TestSchemaParser.PmdXmlReporter; +import net.sourceforge.pmd.util.StringUtil; import com.github.oowekyala.ooxml.DomUtils; import com.github.oowekyala.ooxml.messages.PositionedXmlDoc; @@ -204,7 +206,10 @@ class BaseTestParserImpl { } usedFragments.add(id.getValue()); code = parseTextNodeNoTrim(fragment); - code = code.trim(); // todo replace with trimIndent in PMD 7 + // first trim empty lines at beginning/end + code = code.trim(); + // then trim any indentation + code = StringUtil.trimIndent(Chars.wrap(code)).toString(); } return code; } @@ -225,17 +230,17 @@ class BaseTestParserImpl { } /** FIXME this is stupid, the language version may be of a different language than the Rule... */ - private static LanguageVersion parseSourceType(String terseNameAndVersion) { + private static LanguageVersion parseSourceType(String languageIdAndVersion) { final String version; - final String terseName; - if (terseNameAndVersion.contains(" ")) { - version = StringUtils.trimToNull(terseNameAndVersion.substring(terseNameAndVersion.lastIndexOf(' ') + 1)); - terseName = terseNameAndVersion.substring(0, terseNameAndVersion.lastIndexOf(' ')); + final String languageId; + if (languageIdAndVersion.contains(" ")) { + version = StringUtils.trimToNull(languageIdAndVersion.substring(languageIdAndVersion.lastIndexOf(' ') + 1)); + languageId = languageIdAndVersion.substring(0, languageIdAndVersion.lastIndexOf(' ')); } else { version = null; - terseName = terseNameAndVersion; + languageId = languageIdAndVersion; } - Language language = LanguageRegistry.PMD.getLanguageById(terseName); + Language language = LanguageRegistry.PMD.getLanguageById(languageId); if (language != null) { if (version == null) { return language.getDefaultVersion(); diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java index 0177ebce33..521e68f8c1 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/VfExpressionTypeVisitor.java @@ -42,7 +42,6 @@ class VfExpressionTypeVisitor extends VfVisitorBase { * {@code controller} or {@code extensions} attribute. */ private final List apexClassNames; - // todo make those lists of Path private final List apexDirectories; private final List objectsDirectories; From a8c539eddbc66c5f9d3b6cdd732483d3670861f7 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Tue, 5 Mar 2024 19:10:20 +0100 Subject: [PATCH 2/2] [test-schema] Fix trim indentation for test codes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Juan Martín Sotuyo Dodero --- .../pmd/test/schema/BaseTestParserImpl.java | 10 +++---- .../pmd/test/schema/TestSchemaParserTest.java | 30 +++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java b/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java index 0f26b41bf2..972956e17d 100644 --- a/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java +++ b/pmd-test-schema/src/main/java/net/sourceforge/pmd/test/schema/BaseTestParserImpl.java @@ -98,7 +98,7 @@ class BaseTestParserImpl { if (description == null) { return; } - descriptor.setDescription(description); + descriptor.setDescription(description.trim()); } parseBoolAttribute(testCode, "reinitializeRule", true, err, "Attribute 'reinitializeRule' is deprecated and ignored, assumed true"); @@ -206,11 +206,9 @@ class BaseTestParserImpl { } usedFragments.add(id.getValue()); code = parseTextNodeNoTrim(fragment); - // first trim empty lines at beginning/end - code = code.trim(); - // then trim any indentation - code = StringUtil.trimIndent(Chars.wrap(code)).toString(); } + // first trim empty lines at beginning/end, then trim any indentation + code = StringUtil.trimIndent(Chars.wrap(code).trimBlankLines()).toString(); return code; } @@ -296,7 +294,7 @@ class BaseTestParserImpl { if (node == null) { return null; } - return parseTextNode(node); + return parseTextNodeNoTrim(node); } private Element getSingleChild(Element parentElm, String nodeName, boolean required, PmdXmlReporter err) { diff --git a/pmd-test-schema/src/test/java/net/sourceforge/pmd/test/schema/TestSchemaParserTest.java b/pmd-test-schema/src/test/java/net/sourceforge/pmd/test/schema/TestSchemaParserTest.java index 73878474ae..abe7afcf3a 100644 --- a/pmd-test-schema/src/test/java/net/sourceforge/pmd/test/schema/TestSchemaParserTest.java +++ b/pmd-test-schema/src/test/java/net/sourceforge/pmd/test/schema/TestSchemaParserTest.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.test.schema; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -39,6 +40,7 @@ class TestSchemaParserTest { + " 4\n" + " \n" + " \n" @@ -55,6 +57,34 @@ class TestSchemaParserTest { RuleTestCollection parsed = parseFile(file); assertEquals(2, parsed.getTests().size()); + assertThat("Indentation should be removed", + parsed.getTests().get(0).getCode(), equalTo("public class Foo {\n private int i;\n}")); + } + + @Test + void testSharedCodeFragment() throws IOException { + String file = "\n" + + "\n" + + " \n" + + " \n" + + " equality operators with Double.NaN\n" + + " 4\n" + + " \n" + + " \n" + + "\n"; + + RuleTestCollection parsed = parseFile(file); + + assertEquals(1, parsed.getTests().size()); + assertThat("Indentation should be removed", + parsed.getTests().get(0).getCode(), equalTo("public class Foo {\n private int i;\n}")); } @Test