diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 5f4f460f3f1..acd1d647869 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,6 +18,8 @@ This is a {{ site.pmd.release_type }} release. * apex * [#4096](https://github.com/pmd/pmd/issues/4096): \[apex] ApexAssertionsShouldIncludeMessage and ApexUnitTestClassShouldHaveAsserts: support new Assert class (introduced with Apex v56.0) +* core + * [#3970](https://github.com/pmd/pmd/issues/3970): \[core] FileCollector.addFile ignores language parameter * java-codestyle * [#4082](https://github.com/pmd/pmd/issues/4082): \[java] UnnecessaryImport false positive for on-demand imports of nested classes diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java index c7d77e1de88..16ab46f93ff 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/NioTextFile.java @@ -18,6 +18,7 @@ import net.sourceforge.pmd.util.IOUtil; import net.sourceforge.pmd.util.datasource.DataSource; import net.sourceforge.pmd.util.datasource.FileDataSource; +import net.sourceforge.pmd.util.datasource.internal.LanguageAwareDataSource; /** * A {@link TextFile} backed by a file in some {@link FileSystem}. @@ -73,7 +74,7 @@ public String readContents() throws IOException { @Override public DataSource toDataSourceCompat() { - return new FileDataSource(path.toFile()); + return new LanguageAwareDataSource(new FileDataSource(path.toFile()), languageVersion); } @Override diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/StringTextFile.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/StringTextFile.java index 598823e555c..c4d286d8950 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/StringTextFile.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/document/StringTextFile.java @@ -12,6 +12,7 @@ import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.util.datasource.DataSource; import net.sourceforge.pmd.util.datasource.ReaderDataSource; +import net.sourceforge.pmd.util.datasource.internal.LanguageAwareDataSource; /** * Read-only view on a string. @@ -64,9 +65,10 @@ public String readContents() { @Override public DataSource toDataSourceCompat() { - return new ReaderDataSource( + return new LanguageAwareDataSource(new ReaderDataSource( new StringReader(content), - pathId + pathId), + languageVersion ); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java index da91fbd95a6..e4b1d75de62 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/PmdRunnable.java @@ -21,6 +21,7 @@ import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.renderers.Renderer; import net.sourceforge.pmd.util.datasource.DataSource; +import net.sourceforge.pmd.util.datasource.internal.LanguageAwareDataSource; /** * @@ -82,6 +83,9 @@ public Report call() { try (InputStream stream = new BufferedInputStream(dataSource.getInputStream())) { tc.ruleContext.setLanguageVersion(null); + if (dataSource instanceof LanguageAwareDataSource) { + tc.ruleContext.setLanguageVersion(((LanguageAwareDataSource) dataSource).getLanguageVersion()); + } sourceCodeProcessor.processSourceCode(stream, tc.ruleSets, tc.ruleContext); } catch (PMDException pmde) { addError(report, pmde, "Error while processing file: " + fileName); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/internal/LanguageAwareDataSource.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/internal/LanguageAwareDataSource.java new file mode 100644 index 00000000000..407001e1028 --- /dev/null +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/datasource/internal/LanguageAwareDataSource.java @@ -0,0 +1,42 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.util.datasource.internal; + +import java.io.IOException; +import java.io.InputStream; + +import net.sourceforge.pmd.annotation.InternalApi; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.util.datasource.DataSource; + +@InternalApi +public class LanguageAwareDataSource implements DataSource { + private final DataSource base; // delegate DataSource methods to this + private final LanguageVersion version; + + public LanguageAwareDataSource(DataSource base, LanguageVersion version) { + this.base = base; + this.version = version; + } + + public LanguageVersion getLanguageVersion() { + return version; + } + + @Override + public InputStream getInputStream() throws IOException { + return base.getInputStream(); + } + + @Override + public String getNiceFileName(boolean shortNames, String inputFileName) { + return base.getNiceFileName(shortNames, inputFileName); + } + + @Override + public void close() throws IOException { + base.close(); + } +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java index 4ec5555cf97..a1f2f0f1569 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/PmdAnalysisTest.java @@ -14,10 +14,18 @@ import static org.mockito.Mockito.verify; import java.io.IOException; +import java.nio.file.Paths; +import java.util.List; +import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentMatchers; +import net.sourceforge.pmd.lang.Dummy2LanguageModule; +import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.ast.Node; +import net.sourceforge.pmd.lang.document.SimpleTestTextFile; +import net.sourceforge.pmd.lang.rule.AbstractRule; import net.sourceforge.pmd.renderers.Renderer; /** @@ -72,4 +80,54 @@ public void testRulesetWhenSomeoneHasAnError() { } } + @Test + public void testFileWithSpecificLanguage() { + final Language language = Dummy2LanguageModule.getInstance(); + PMDConfiguration config = new PMDConfiguration(); + config.setIgnoreIncrementalAnalysis(true); + RuleSet ruleset = RuleSet.forSingleRule(new TestRule()); + + try (PmdAnalysis pmd = PmdAnalysis.create(config)) { + pmd.addRuleSet(ruleset); + pmd.files().addFile(Paths.get("src", "test", "resources", "sample-source", "dummy", "foo.txt"), language); + Report report = pmd.performAnalysisAndCollectReport(); + for (Report.ProcessingError error : report.getProcessingErrors()) { + System.out.println("error = " + error.getMsg() + ": " + error.getDetail()); + } + Assert.assertEquals(0, report.getProcessingErrors().size()); + Assert.assertEquals(1, report.getViolations().size()); + } + } + + @Test + public void testTextFileWithSpecificLanguage() { + final Language language = Dummy2LanguageModule.getInstance(); + PMDConfiguration config = new PMDConfiguration(); + config.setIgnoreIncrementalAnalysis(true); + RuleSet ruleset = RuleSet.forSingleRule(new TestRule()); + + try (PmdAnalysis pmd = PmdAnalysis.create(config)) { + pmd.addRuleSet(ruleset); + pmd.files().addFile(new SimpleTestTextFile("test content foo", "foo.txt", "foo.txt", language.getDefaultVersion())); + Report report = pmd.performAnalysisAndCollectReport(); + for (Report.ProcessingError error : report.getProcessingErrors()) { + System.out.println("error = " + error.getMsg() + ": " + error.getDetail()); + } + Assert.assertEquals(0, report.getProcessingErrors().size()); + Assert.assertEquals(1, report.getViolations().size()); + } + } + + public static class TestRule extends AbstractRule { + public TestRule() { + setLanguage(Dummy2LanguageModule.getInstance()); + setMessage("dummy 2 test rule"); + } + + @Override + public void apply(List nodes, RuleContext ctx) { + ctx.addViolation(nodes.get(0)); + } + } + } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/Dummy2LanguageModule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/Dummy2LanguageModule.java index e5a8de77cb6..9d71d220101 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/Dummy2LanguageModule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/Dummy2LanguageModule.java @@ -16,4 +16,8 @@ public Dummy2LanguageModule() { super(NAME, null, TERSE_NAME, "dummy2"); addVersion("1.0", new DummyLanguageModule.Handler(), true); } + + public static Language getInstance() { + return LanguageRegistry.getLanguage(NAME); + } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/SimpleTestTextFile.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/SimpleTestTextFile.java new file mode 100644 index 00000000000..6f663471427 --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/document/SimpleTestTextFile.java @@ -0,0 +1,17 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.document; + +import net.sourceforge.pmd.lang.LanguageVersion; + +/** + * Makes {@link StringTextFile} publicly available for unit tests. + */ +public class SimpleTestTextFile extends StringTextFile { + + public SimpleTestTextFile(String content, String pathId, String displayName, LanguageVersion languageVersion) { + super(content, pathId, displayName, languageVersion); + } +} diff --git a/pmd-core/src/test/resources/sample-source/dummy/foo.txt b/pmd-core/src/test/resources/sample-source/dummy/foo.txt new file mode 100644 index 00000000000..adaad871778 --- /dev/null +++ b/pmd-core/src/test/resources/sample-source/dummy/foo.txt @@ -0,0 +1 @@ +A dummy file with file extension txt.