diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java index 5b005f20f5..466fc5e92d 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PMDConfiguration.java @@ -335,7 +335,8 @@ public class PMDConfiguration extends AbstractConfiguration { */ public void setDefaultLanguageVersion(LanguageVersion languageVersion) { Objects.requireNonNull(languageVersion); - setDefaultLanguageVersions(Arrays.asList(languageVersion)); + languageVersionDiscoverer.setDefaultLanguageVersion(languageVersion); + getLanguageProperties(languageVersion.getLanguage()).setLanguageVersion(languageVersion.getVersion()); } /** @@ -347,8 +348,7 @@ public class PMDConfiguration extends AbstractConfiguration { */ public void setDefaultLanguageVersions(List languageVersions) { for (LanguageVersion languageVersion : languageVersions) { - Objects.requireNonNull(languageVersion); - languageVersionDiscoverer.setDefaultLanguageVersion(languageVersion); + setDefaultLanguageVersion(languageVersion); } } 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 d3a787024f..9f82503a15 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/PmdAnalysis.java @@ -144,6 +144,12 @@ public final class PmdAnalysis implements AutoCloseable { LanguagePropertyBundle props = config.getLanguageProperties(language); assert props.getLanguage().equals(language); pmd.langProperties.put(language, props); + + LanguageVersion forcedVersion = config.getForceLanguageVersion(); + if (forcedVersion != null && forcedVersion.getLanguage().equals(language)) { + props.setLanguageVersion(forcedVersion.getVersion()); + } + // TODO replace those with actual language properties when the // CLI syntax is implemented. props.setProperty(LanguagePropertyBundle.SUPPRESS_MARKER, config.getSuppressMarker()); diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java index e37c849670..5bc699b316 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/Rule.java @@ -7,6 +7,7 @@ package net.sourceforge.pmd; import java.util.List; import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.RuleTargetSelector; @@ -255,6 +256,14 @@ public interface Rule extends PropertySource { */ RuleTargetSelector getTargetSelector(); + /** + * Initialize the rule using the language processor if needed. + * + * @param languageProcessor The processor for the rule's language + */ + default void initialize(LanguageProcessor languageProcessor) { + // by default do nothing + } /** * Start processing. Called once, before apply() is first called. diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java index 8ba5ed5b30..00ad1b5b84 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/AbstractPmdLanguageVersionHandler.java @@ -4,12 +4,6 @@ package net.sourceforge.pmd.lang; -import java.util.Locale; - -import net.sourceforge.pmd.properties.PropertyDescriptor; -import net.sourceforge.pmd.properties.PropertySource; - - /** * Base language version handler for languages that support PMD, i.e. can build an AST * and support AST processing stages. @@ -20,57 +14,4 @@ import net.sourceforge.pmd.properties.PropertySource; public abstract class AbstractPmdLanguageVersionHandler extends AbstractLanguageVersionHandler { - public static void readLanguagePropertiesFromEnv(LanguagePropertyBundle props) { - for (PropertyDescriptor propertyDescriptor : props.getPropertyDescriptors()) { - String propertyValue = getEnvValue(props.getLanguage().getTerseName(), propertyDescriptor); - - if (propertyValue != null) { - setPropertyCapture(props, propertyDescriptor, propertyValue); - } - } - } - - /** - * Returns the environment variable name that a user can set in order to override the default value. - */ - static String getEnvironmentVariableName(String langTerseName, PropertyDescriptor propertyDescriptor) { - if (langTerseName == null) { - throw new IllegalStateException("Language is null"); - } - return "PMD_" + langTerseName.toUpperCase(Locale.ROOT) + "_" - + propertyDescriptor.name().toUpperCase(Locale.ROOT); - } - - /** - * @return environment variable that overrides the PropertyDesciptors default value. Returns null if no environment - * variable has been set. - */ - - static String getEnvValue(String langTerseName, PropertyDescriptor propertyDescriptor) { - // note: since we use environent variables and not system properties, - // tests override this method. - return System.getenv(getEnvironmentVariableName(langTerseName, propertyDescriptor)); - } - - /** - * Overrides the default PropertyDescriptors with values found in environment variables. - * TODO: Move this to net.sourceforge.pmd.PMD#parserFor when CLI options are implemented - */ - @Deprecated - protected final void overridePropertiesFromEnv(String langTerseName, PropertySource source) { - for (PropertyDescriptor propertyDescriptor : source.getPropertyDescriptors()) { - String propertyValue = getEnvValue(langTerseName, propertyDescriptor); - - if (propertyValue != null) { - setPropertyCapture(source, propertyDescriptor, propertyValue); - } - } - } - - @Deprecated - private static void setPropertyCapture(PropertySource source, PropertyDescriptor propertyDescriptor, String propertyValue) { - T value = propertyDescriptor.valueFrom(propertyValue); - source.setProperty(propertyDescriptor, value); - } - } 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 afc90d9448..56a301cd5f 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 @@ -7,6 +7,7 @@ package net.sourceforge.pmd.lang; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Properties; @@ -23,7 +24,7 @@ import net.sourceforge.pmd.util.log.MessageReporter; * * @author Clément Fournier */ -public class LanguageProcessorRegistry implements Iterable, AutoCloseable { +public final class LanguageProcessorRegistry implements Iterable, AutoCloseable { private final Map processors; @@ -56,7 +57,11 @@ public class LanguageProcessorRegistry implements Iterable, AutoClosea for (Language language : registry) { LanguagePropertyBundle properties = languageProperties.getOrDefault(language, language.newPropertyBundle()); try { - assert properties.getLanguage().equals(language): "Mismatched language"; + assert properties.getLanguage().equals(language) : "Mismatched language"; + + readLanguagePropertiesFromEnv(properties, messageReporter); + + @SuppressWarnings("PMD.CloseResource") LanguageProcessor processor = language.createProcessor(properties); processors.put(language, processor); } catch (IllegalArgumentException e) { @@ -67,7 +72,8 @@ public class LanguageProcessorRegistry implements Iterable, AutoClosea return new LanguageProcessorRegistry(processors); } - private static Map derivePropertiesFromStrings( + // TODO this should be reused when implementing the CLI + public static Map derivePropertiesFromStrings( Map stringProperties, MessageReporter reporter ) { @@ -111,8 +117,42 @@ public class LanguageProcessorRegistry implements Iterable, AutoClosea } } + // transitional until the CLI supports setting language properties + @Deprecated + public static void readLanguagePropertiesFromEnv(LanguagePropertyBundle props, MessageReporter reporter) { + for (PropertyDescriptor propertyDescriptor : props.getPropertyDescriptors()) { + String propertyValue = getEnvValue(props.getLanguage().getTerseName(), propertyDescriptor); + + if (propertyValue != null) { + trySetPropertyCapture(props, propertyDescriptor, propertyValue, reporter); + } + } + } + + /** + * Returns the environment variable name that a user can set in order to override the default value. + */ + private static String getEnvironmentVariableName(String langTerseName, PropertyDescriptor propertyDescriptor) { + if (langTerseName == null) { + throw new IllegalStateException("Language is null"); + } + return "PMD_" + langTerseName.toUpperCase(Locale.ROOT) + "_" + + propertyDescriptor.name().toUpperCase(Locale.ROOT); + } + + /** + * @return environment variable that overrides the PropertyDesciptors default value. Returns null if no environment + * variable has been set. + */ + private static String getEnvValue(String langTerseName, PropertyDescriptor propertyDescriptor) { + // note: since we use environent variables and not system properties, + // tests override this method. + return System.getenv(getEnvironmentVariableName(langTerseName, propertyDescriptor)); + } + public static class LanguageTerminationException extends RuntimeException { + public LanguageTerminationException(Throwable cause) { super(cause); } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java index 1b228e8dc6..0c65e32ce3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/lang/LanguageVersionHandler.java @@ -10,7 +10,6 @@ import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory; import net.sourceforge.pmd.lang.rule.xpath.impl.XPathHandler; -import net.sourceforge.pmd.properties.PropertySource; import net.sourceforge.pmd.util.designerbindings.DesignerBindings; import net.sourceforge.pmd.util.designerbindings.DesignerBindings.DefaultDesignerBindings; @@ -32,15 +31,6 @@ public interface LanguageVersionHandler { } - /** - * @deprecated This is transitional - */ - @Deprecated - default void declareParserTaskProperties(PropertySource source) { - // do nothing - } - - /** * Get the Parser. * diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java index 8cdf297a12..42d30009a3 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/processor/AbstractPMDProcessor.java @@ -4,23 +4,10 @@ package net.sourceforge.pmd.processor; -import static java.util.Collections.emptyMap; -import static net.sourceforge.pmd.util.CollectionUtil.listOf; -import static net.sourceforge.pmd.util.CollectionUtil.setOf; - -import java.util.List; - -import net.sourceforge.pmd.PMDConfiguration; -import net.sourceforge.pmd.RuleSet; -import net.sourceforge.pmd.RuleSets; import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguageProcessor.AnalysisTask; -import net.sourceforge.pmd.lang.LanguageProcessorRegistry; -import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.document.TextFile; -import net.sourceforge.pmd.reporting.GlobalAnalysisListener; -import net.sourceforge.pmd.util.log.MessageReporter; /** * This is internal API! @@ -61,35 +48,4 @@ public abstract class AbstractPMDProcessor implements AutoCloseable { : new MonoThreadProcessor(analysisTask, processor); } - /** - * This is provided as convenience for tests. The listener is not closed. - * It executes the rulesets on this thread, without copying the rulesets. - */ - @InternalApi - public static void runSingleFile(List ruleSets, - TextFile file, - GlobalAnalysisListener listener, - PMDConfiguration configuration) throws Exception { - RuleSets rsets = new RuleSets(ruleSets); - MessageReporter reporter = configuration.getReporter(); - - - LanguageRegistry singletonReg= new LanguageRegistry(setOf(file.getLanguageVersion().getLanguage())); - try (LanguageProcessorRegistry registry = - LanguageProcessorRegistry.create(singletonReg, - emptyMap(), - reporter)) { - - LanguageProcessor lprocessor = registry.getProcessor(file.getLanguageVersion().getLanguage()); - lprocessor.launchAnalysis(new AnalysisTask( - rsets, - listOf(file), - listener, - 1, - configuration.getAnalysisCache(), - reporter - )).close(); - - } - } } diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java index 9cd086f186..a0265798ae 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/util/treeexport/TreeExportCli.java @@ -21,10 +21,9 @@ import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.annotation.Experimental; import net.sourceforge.pmd.internal.Slf4jSimpleConfiguration; import net.sourceforge.pmd.lang.Language; +import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; -import net.sourceforge.pmd.lang.LanguageVersionHandler; -import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.ast.RootNode; import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; @@ -180,7 +179,7 @@ public class TreeExportCli { run(LanguageRegistry.PMD, renderer); } - private void run(LanguageRegistry registry, TreeRenderer renderer) throws IOException { + private void run(LanguageRegistry registry, TreeRenderer renderer) { printWarning(); Language lang = registry.getLanguageById(language); @@ -191,8 +190,6 @@ public class TreeExportCli { } LanguageVersion langVersion = lang.getDefaultVersion(); - LanguageVersionHandler languageHandler = langVersion.getLanguageVersionHandler(); - Parser parser = languageHandler.getParser(); @SuppressWarnings("PMD.CloseResource") TextFile textFile; if (file == null && !readStdin) { @@ -207,12 +204,16 @@ public class TreeExportCli { // disable warnings for deprecated attributes Slf4jSimpleConfiguration.disableLogging(Attribute.class); - try (TextDocument textDocument = TextDocument.create(textFile)) { + try ( + LanguageProcessor processor = lang.createProcessor(lang.newPropertyBundle()); + TextDocument textDocument = TextDocument.create(textFile)) { - ParserTask task = new ParserTask(textDocument, SemanticErrorReporter.noop(), TreeExportCli.class.getClassLoader()); - RootNode root = parser.parse(task); + ParserTask task = new ParserTask(textDocument, SemanticErrorReporter.noop()); + RootNode root = processor.services().getParser().parse(task); renderer.renderSubtree(root, io.stdout); + } catch (Exception e) { + throw new RuntimeException(e); } } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/AbstractRuleTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/AbstractRuleTest.java index acb88be6ef..00fabf9d42 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/AbstractRuleTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/AbstractRuleTest.java @@ -12,9 +12,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import java.util.Collections; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import net.sourceforge.pmd.Report.SuppressedViolation; -import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; @@ -66,11 +66,14 @@ public class AbstractRuleTest { } } + @RegisterExtension + private final DummyParsingHelper helper = new DummyParsingHelper(); + @Test void testCreateRV() { MyRule r = new MyRule(); r.setRuleSetName("foo"); - DummyRootNode s = DummyLanguageModule.parse("abc()", "filename"); + DummyRootNode s = helper.parse("abc()", "filename"); RuleViolation rv = new ParametricRuleViolation(r, s, r.getMessage()); assertEquals(1, rv.getBeginLine(), "Line number mismatch!"); @@ -83,7 +86,7 @@ public class AbstractRuleTest { @Test void testCreateRV2() { MyRule r = new MyRule(); - DummyRootNode s = DummyLanguageModule.parse("abc()", "filename"); + DummyRootNode s = helper.parse("abc()", "filename"); RuleViolation rv = new ParametricRuleViolation(r, s, "specificdescription"); assertEquals(1, rv.getBeginLine(), "Line number mismatch!"); assertEquals("filename", rv.getFilename(), "Filename mismatch!"); @@ -102,7 +105,7 @@ public class AbstractRuleTest { r.definePropertyDescriptor(PropertyFactory.intProperty("testInt").desc("description").require(inRange(0, 100)).defaultValue(10).build()); r.setMessage("Message ${packageName} ${className} ${methodName} ${variableName} ${testInt} ${noSuchProperty}"); - DummyRootNode s = DummyLanguageModule.parse("abc()", "filename"); + DummyRootNode s = helper.parse("abc()", "filename"); RuleViolation rv = RuleContextTest.getReportForRuleApply(r, s).getViolations().get(0); assertEquals("Message foo 10 ${noSuchProperty}", rv.getDescription()); @@ -110,8 +113,8 @@ public class AbstractRuleTest { @Test void testRuleSuppress() { - DummyRootNode n = DummyLanguageModule.parse("abc()", "filename") - .withNoPmdComments(Collections.singletonMap(1, "ohio")); + DummyRootNode n = helper.parse("abc()", "filename") + .withNoPmdComments(Collections.singletonMap(1, "ohio")); RuleViolation violation = DefaultRuleViolationFactory.defaultInstance().createViolation(new MyRule(), n, n.getReportLocation(), "specificdescription"); SuppressedViolation suppressed = DefaultRuleViolationFactory.defaultInstance().suppressOrNull(n, violation); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/DummyParsingHelper.java b/pmd-core/src/test/java/net/sourceforge/pmd/DummyParsingHelper.java new file mode 100644 index 0000000000..7d8090205d --- /dev/null +++ b/pmd-core/src/test/java/net/sourceforge/pmd/DummyParsingHelper.java @@ -0,0 +1,64 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd; + +import java.util.Collections; + +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; + +import net.sourceforge.pmd.lang.DummyLanguageModule; +import net.sourceforge.pmd.lang.LanguageProcessor; +import net.sourceforge.pmd.lang.LanguageProcessorRegistry; +import net.sourceforge.pmd.lang.LanguageRegistry; +import net.sourceforge.pmd.lang.LanguageVersion; +import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode; +import net.sourceforge.pmd.lang.ast.Parser.ParserTask; +import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; +import net.sourceforge.pmd.lang.document.TextDocument; +import net.sourceforge.pmd.lang.document.TextFile; +import net.sourceforge.pmd.util.log.internal.NoopReporter; + +/** + * @author Clément Fournier + */ +public class DummyParsingHelper implements Extension, BeforeEachCallback, AfterEachCallback { + + private LanguageProcessor dummyProcessor; + + public DummyParsingHelper() { + + } + + public DummyRootNode parse(String code) { + return parse(code, TextFile.UNKNOWN_FILENAME); + } + + public DummyRootNode parse(String code, String filename) { + LanguageVersion version = DummyLanguageModule.getInstance().getDefaultVersion(); + ParserTask task = new ParserTask( + TextDocument.readOnlyString(code, filename, version), + SemanticErrorReporter.noop() + ); + return (DummyRootNode) dummyProcessor.services().getParser().parse(task); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + dummyProcessor.close(); + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + LanguageProcessorRegistry registry = LanguageProcessorRegistry.create( + LanguageRegistry.PMD, + Collections.emptyMap(), + new NoopReporter() + ); + dummyProcessor = registry.getProcessor(DummyLanguageModule.getInstance()); + } +} diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java index 81396be626..321e7af295 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleSetTest.java @@ -37,6 +37,7 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import net.sourceforge.pmd.Report.ProcessingError; import net.sourceforge.pmd.RuleSet.RuleSetBuilder; @@ -51,6 +52,9 @@ import net.sourceforge.pmd.util.IOUtil; class RuleSetTest { + @RegisterExtension + private final DummyParsingHelper helper = new DummyParsingHelper(); + @Test void testRuleSetRequiresName() { assertThrows(NullPointerException.class, () -> @@ -477,14 +481,9 @@ class RuleSetTest { } private RootNode makeCompilationUnits(String filename) { - DummyRootNode node = DummyLanguageModule.parse("dummyCode", filename); + DummyRootNode node = helper.parse("dummyCode", filename); node.setImage("Foo"); return node; - // DummyRootNode node = new DummyRootNode(); - // node.setCoordsReplaceText(1, 1, 2, 1); - // node.setImage("Foo"); - // node.withFileName(filename); - // return node; } @Test diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/RuleViolationTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/RuleViolationTest.java index 90fe620de6..a3e46d8043 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/RuleViolationTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/RuleViolationTest.java @@ -10,8 +10,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.Comparator; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; -import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode; import net.sourceforge.pmd.lang.document.FileLocation; @@ -22,10 +22,13 @@ import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; class RuleViolationTest { + @RegisterExtension + private final DummyParsingHelper helper = new DummyParsingHelper(); + @Test void testConstructor1() { Rule rule = new MockRule("name", "desc", "msg", "rulesetname"); - DummyRootNode s = DummyLanguageModule.parse("abcd", "filename"); + DummyRootNode s = helper.parse("abcd", "filename"); RuleViolation r = new ParametricRuleViolation(rule, s, rule.getMessage()); assertEquals(rule, r.getRule(), "object mismatch"); assertEquals(1, r.getBeginLine(), "line number is wrong"); @@ -35,7 +38,7 @@ class RuleViolationTest { @Test void testConstructor2() { Rule rule = new MockRule("name", "desc", "msg", "rulesetname"); - DummyRootNode s = DummyLanguageModule.parse("abcd", "filename"); + DummyRootNode s = helper.parse("abcd", "filename"); RuleViolation r = new ParametricRuleViolation(rule, s, "description"); assertEquals(rule, r.getRule(), "object mismatch"); assertEquals(1, r.getBeginLine(), "line number is wrong"); @@ -47,8 +50,8 @@ class RuleViolationTest { void testComparatorWithDifferentFilenames() { Rule rule = new MockRule("name", "desc", "msg", "rulesetname"); Comparator comp = RuleViolation.DEFAULT_COMPARATOR; - DummyNode s = DummyLanguageModule.parse("(abc)", "filename1").getFirstChild(); - DummyNode s1 = DummyLanguageModule.parse("(abc)", "filename2").getFirstChild(); + DummyNode s = helper.parse("(abc)", "filename1").getFirstChild(); + DummyNode s1 = helper.parse("(abc)", "filename2").getFirstChild(); RuleViolation r1 = new ParametricRuleViolation(rule, s, "description"); RuleViolation r2 = new ParametricRuleViolation(rule, s1, "description"); assertEquals(-1, comp.compare(r1, r2)); @@ -59,7 +62,7 @@ class RuleViolationTest { void testComparatorWithSameFileDifferentLines() { Rule rule = new MockRule("name", "desc", "msg", "rulesetname"); Comparator comp = RuleViolation.DEFAULT_COMPARATOR; - DummyRootNode root = DummyLanguageModule.parse("(abc) (def)"); + DummyRootNode root = helper.parse("(abc) (def)"); DummyNode abcChild = root.getChild(0); DummyNode defChild = root.getChild(1); RuleViolation r1 = new ParametricRuleViolation(rule, abcChild, "description"); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java index 4e639fb5e1..64fb1ce651 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/DummyLanguageModule.java @@ -16,11 +16,9 @@ import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.ast.ParseException; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; -import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.FileLocation; import net.sourceforge.pmd.lang.document.TextDocument; -import net.sourceforge.pmd.lang.document.TextFile; import net.sourceforge.pmd.lang.document.TextRegion; import net.sourceforge.pmd.lang.rule.ParametricRuleViolation; import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory; @@ -52,18 +50,6 @@ public class DummyLanguageModule extends BaseLanguageModule { return (DummyLanguageModule) Objects.requireNonNull(LanguageRegistry.PMD.getLanguageByFullName(NAME)); } - public static DummyRootNode parse(String code) { - return parse(code, TextFile.UNKNOWN_FILENAME); - } - - public static DummyRootNode parse(String code, String filename) { - LanguageVersion version = DummyLanguageModule.getInstance().getDefaultVersion(); - ParserTask task = new ParserTask( - TextDocument.readOnlyString(code, filename, version), - SemanticErrorReporter.noop() - ); - return (DummyRootNode) version.getLanguageVersionHandler().getParser().parse(task); - } public static class Handler extends AbstractPmdLanguageVersionHandler { diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/XPathRuleTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/XPathRuleTest.java index 56621fc092..7a425eb587 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/XPathRuleTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/XPathRuleTest.java @@ -11,10 +11,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import net.sourceforge.pmd.DummyParsingHelper; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.RuleContextTest; -import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode; import net.sourceforge.pmd.lang.ast.DummyNodeWithDeprecatedAttribute; @@ -25,6 +26,9 @@ import com.github.stefanbirkner.systemlambda.SystemLambda; class XPathRuleTest { + @RegisterExtension + private final DummyParsingHelper helper = new DummyParsingHelper(); + @Test void testAttributeDeprecation10() throws Exception { testDeprecation(XPathVersion.XPATH_1_0); @@ -148,7 +152,7 @@ class XPathRuleTest { } public DummyRootNode newRoot(String fileName) { - return DummyLanguageModule.parse("dummy code", fileName); + return helper.parse("dummy code", fileName); } diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/ElementNodeTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/ElementNodeTest.java index 3cb4065cf9..d838c7c230 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/ElementNodeTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/lang/rule/xpath/internal/ElementNodeTest.java @@ -11,8 +11,9 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; -import net.sourceforge.pmd.lang.DummyLanguageModule; +import net.sourceforge.pmd.DummyParsingHelper; import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode; @@ -22,9 +23,12 @@ import net.sf.saxon.type.Type; class ElementNodeTest { + @RegisterExtension + private final DummyParsingHelper helper = new DummyParsingHelper(); + @Test void testCompareOrder() { - DummyRootNode root = DummyLanguageModule.parse( + DummyRootNode root = helper.parse( "(#foo)" + "(#foo)" ); @@ -62,7 +66,7 @@ class ElementNodeTest { @Test void verifyTextNodeType() { - DummyRootNode root = DummyLanguageModule.parse("(foo)(#text)"); + DummyRootNode root = helper.parse("(foo)(#text)"); DummyNode c0 = root.getChild(0); DummyNode c1 = root.getChild(1); @@ -88,7 +92,7 @@ class ElementNodeTest { @Test void verifyCommentNodeType() { - DummyRootNode root = DummyLanguageModule.parse("(#comment)"); + DummyRootNode root = helper.parse("(#comment)"); DummyNode c1 = root.getChild(0); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java index bbb524284b..ac2a0cbc9d 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/AbstractRendererTest.java @@ -16,8 +16,10 @@ import java.nio.file.Path; import java.util.function.Consumer; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.io.TempDir; +import net.sourceforge.pmd.DummyParsingHelper; import net.sourceforge.pmd.FooRule; import net.sourceforge.pmd.Report; import net.sourceforge.pmd.Report.ConfigurationError; @@ -38,6 +40,9 @@ import net.sourceforge.pmd.util.IOUtil; abstract class AbstractRendererTest { + @RegisterExtension + protected final DummyParsingHelper helper = new DummyParsingHelper(); + @TempDir private Path tempDir; diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SummaryHTMLRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SummaryHTMLRendererTest.java index 5ed067de6b..db88db3d72 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SummaryHTMLRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/renderers/SummaryHTMLRendererTest.java @@ -16,9 +16,7 @@ import net.sourceforge.pmd.PMD; import net.sourceforge.pmd.Report.ConfigurationError; import net.sourceforge.pmd.Report.ProcessingError; import net.sourceforge.pmd.RuleContext; -import net.sourceforge.pmd.lang.DummyLanguageModule; import net.sourceforge.pmd.lang.ast.DummyNode.DummyRootNode; -import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.reporting.FileAnalysisListener; class SummaryHTMLRendererTest extends AbstractRendererTest { @@ -147,13 +145,8 @@ class SummaryHTMLRendererTest extends AbstractRendererTest { private Consumer createEmptyReportWithSuppression() { return listener -> { - TextDocument doc = TextDocument.readOnlyString( - "dummy code", - getSourceCodeFilename(), - DummyLanguageModule.getInstance().getDefaultVersion() - ); - DummyRootNode root = DummyLanguageModule.parse("dummy code", getSourceCodeFilename()) - .withNoPmdComments(Collections.singletonMap(1, "test")); + DummyRootNode root = helper.parse("dummy code", getSourceCodeFilename()) + .withNoPmdComments(Collections.singletonMap(1, "test")); RuleContext ruleContext = RuleContext.create(listener, new FooRule()); ruleContext.addViolationWithPosition(root, 1, 1, "suppress test"); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeRenderersTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeRenderersTest.java index bb49ca48a9..fb1863a067 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeRenderersTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/TreeRenderersTest.java @@ -11,8 +11,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; -import net.sourceforge.pmd.lang.DummyLanguageModule; +import net.sourceforge.pmd.DummyParsingHelper; import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.properties.PropertySource; @@ -21,6 +22,9 @@ import net.sourceforge.pmd.properties.PropertySource; */ class TreeRenderersTest { + @RegisterExtension + private final DummyParsingHelper helper = new DummyParsingHelper(); + @Test void testStandardRenderersAreRegistered() { @@ -55,7 +59,7 @@ class TreeRenderersTest { StringBuilder out = new StringBuilder(); - renderer.renderSubtree(dummyTree1(), out); + renderer.renderSubtree(dummyTree1(helper), out); assertEquals("\n" + " \n" + " \n" @@ -64,8 +68,8 @@ class TreeRenderersTest { } - static DummyNode dummyTree1() { - DummyNode dummy = DummyLanguageModule.parse("(parent(child1)(child2))").getChild(0); + static DummyNode dummyTree1(DummyParsingHelper helper) { + DummyNode dummy = helper.parse("(parent(child1)(child2))").getChild(0); dummy.clearXPathAttributes(); dummy.setXPathAttribute("foo", "bar"); dummy.setXPathAttribute("ohio", "4"); diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/XmlTreeRendererTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/XmlTreeRendererTest.java index 9f7083c1c5..49986694c6 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/XmlTreeRendererTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/util/treeexport/XmlTreeRendererTest.java @@ -10,7 +10,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.IOException; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import net.sourceforge.pmd.DummyParsingHelper; import net.sourceforge.pmd.lang.ast.DummyNode; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.xpath.Attribute; @@ -20,10 +22,17 @@ import net.sourceforge.pmd.util.treeexport.XmlTreeRenderer.XmlRenderingConfig; */ class XmlTreeRendererTest { + @RegisterExtension + private final DummyParsingHelper helper = new DummyParsingHelper(); + + DummyNode dummyTree1() { + return TreeRenderersTest.dummyTree1(helper); + } + @Test void testRenderWithAttributes() throws IOException { - DummyNode dummy = TreeRenderersTest.dummyTree1(); + DummyNode dummy = dummyTree1(); XmlRenderingConfig strat = new XmlRenderingConfig(); strat.lineSeparator("\n"); @@ -45,7 +54,7 @@ class XmlTreeRendererTest { @Test void testRenderWithCustomLineSep() throws IOException { - DummyNode dummy = TreeRenderersTest.dummyTree1(); + DummyNode dummy = dummyTree1(); XmlRenderingConfig strat = new XmlRenderingConfig(); strat.lineSeparator("\r\n"); @@ -67,7 +76,7 @@ class XmlTreeRendererTest { @Test void testRenderWithCustomIndent() throws IOException { - DummyNode dummy = TreeRenderersTest.dummyTree1(); + DummyNode dummy = dummyTree1(); XmlRenderingConfig strat = new XmlRenderingConfig().lineSeparator("").indentWith(""); @@ -89,7 +98,7 @@ class XmlTreeRendererTest { @Test void testRenderWithNoAttributes() throws IOException { - DummyNode dummy = TreeRenderersTest.dummyTree1(); + DummyNode dummy = dummyTree1(); XmlRenderingConfig strat = new XmlRenderingConfig() { @Override @@ -116,7 +125,7 @@ class XmlTreeRendererTest { @Test void testRenderFilterAttributes() throws IOException { - DummyNode dummy = TreeRenderersTest.dummyTree1(); + DummyNode dummy = dummyTree1(); XmlRenderingConfig strategy = new XmlRenderingConfig() { @Override @@ -142,7 +151,7 @@ class XmlTreeRendererTest { @Test void testInvalidAttributeName() throws IOException { - DummyNode dummy = TreeRenderersTest.dummyTree1(); + DummyNode dummy = dummyTree1(); dummy.setXPathAttribute("¬AName", "foo"); @@ -161,7 +170,7 @@ class XmlTreeRendererTest { @Test void testEscapeAttributes() throws IOException { - DummyNode dummy = TreeRenderersTest.dummyTree1(); + DummyNode dummy = dummyTree1(); dummy.setXPathAttribute("eh", " 'a &> b\" "); @@ -184,7 +193,7 @@ class XmlTreeRendererTest { @Test void testEscapeDoubleAttributes() throws IOException { - DummyNode dummy = TreeRenderersTest.dummyTree1(); + DummyNode dummy = dummyTree1(); dummy.setXPathAttribute("eh", " 'a &> b\" "); @@ -207,7 +216,7 @@ class XmlTreeRendererTest { @Test void testNoProlog() throws IOException { - DummyNode dummy = TreeRenderersTest.dummyTree1(); + DummyNode dummy = dummyTree1(); XmlRenderingConfig strat = new XmlRenderingConfig().lineSeparator("\n").renderProlog(false); @@ -229,7 +238,7 @@ class XmlTreeRendererTest { @Test void testDefaultLineSep() throws IOException { - DummyNode dummy = TreeRenderersTest.dummyTree1(); + DummyNode dummy = dummyTree1(); XmlTreeRenderer renderer = new XmlTreeRenderer(); diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java index 2ff75dc9b8..9005056eaa 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/JavaParser.java @@ -61,11 +61,7 @@ public class JavaParser extends JjtreeParserAdapter { checker.check(root); if (postProcess) { - JavaAstProcessor processor = - javaProcessor != null ? JavaAstProcessor.create(javaProcessor, task.getReporter()) - : JavaAstProcessor.create(JavaParser.class.getClassLoader(), - task.getLanguageVersion(), - task.getReporter()); + JavaAstProcessor processor = JavaAstProcessor.create(javaProcessor, task.getReporter()); processor.process(root); } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaAstProcessor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaAstProcessor.java index 0b12c3b6ea..d71b3152cb 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaAstProcessor.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaAstProcessor.java @@ -6,13 +6,8 @@ package net.sourceforge.pmd.lang.java.internal; import static net.sourceforge.pmd.lang.java.symbols.table.internal.JavaSemanticErrors.CANNOT_RESOLVE_SYMBOL; -import java.util.IdentityHashMap; -import java.util.Locale; -import java.util.Map; - import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import org.slf4j.event.Level; import net.sourceforge.pmd.benchmark.TimeTracker; import net.sourceforge.pmd.lang.LanguageVersion; @@ -30,8 +25,6 @@ import net.sourceforge.pmd.lang.java.symbols.table.internal.ReferenceCtx; import net.sourceforge.pmd.lang.java.symbols.table.internal.SymbolTableResolver; import net.sourceforge.pmd.lang.java.types.TypeSystem; import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger; -import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger.SimpleLogger; -import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger.VerboseLogger; /** * Processes the output of the parser before rules get access to the AST. @@ -45,27 +38,6 @@ import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger.Ve * using {@link InternalApiBridge#getProcessor(JavaNode)}. */ public final class JavaAstProcessor { - - /** - * FIXME get rid of that, this prevents both ClassLoader and TypeSystem - * to be garbage-collected, which is an important memory leak. Will be - * fixed by https://github.com/pmd/pmd/issues/3782 (Language Lifecycle) - */ - private static final Map TYPE_SYSTEMS = new IdentityHashMap<>(); - private static final Level INFERENCE_LOG_LEVEL; - - - static { - Level level; - try { - level = Level.valueOf(System.getenv("PMD_DEBUG_LEVEL").toLowerCase(Locale.ROOT)); - } catch (IllegalArgumentException | NullPointerException ignored) { - level = null; - } - INFERENCE_LOG_LEVEL = level; - } - - private final TypeInferenceLogger typeInferenceLogger; private final SemanticErrorReporter logger; private final LanguageVersion languageVersion; @@ -88,23 +60,13 @@ public final class JavaAstProcessor { this.languageVersion = languageVersion; this.typeSystem = typeSystem; - unresolvedTypes = new UnresolvedClassStore(typeSystem); + this.unresolvedTypes = new UnresolvedClassStore(typeSystem); } public UnresolvedClassStore getUnresolvedStore() { return unresolvedTypes; } - static TypeInferenceLogger defaultTypeInfLogger() { - if (INFERENCE_LOG_LEVEL == Level.TRACE) { - return new VerboseLogger(System.err); - } else if (INFERENCE_LOG_LEVEL == Level.DEBUG) { - return new SimpleLogger(System.err); - } else { - return TypeInferenceLogger.noop(); - } - } - /** * Find a symbol from the auxclasspath. If not found, will create @@ -186,62 +148,23 @@ public final class JavaAstProcessor { return typeSystem; } - public static JavaAstProcessor create(SymbolResolver symResolver, - TypeSystem typeSystem, - LanguageVersion languageVersion, - SemanticErrorReporter logger) { - - return new JavaAstProcessor( - typeSystem, - symResolver, - logger, - defaultTypeInfLogger(), - languageVersion - ); - } - - public static JavaAstProcessor create(ClassLoader classLoader, - LanguageVersion languageVersion, - SemanticErrorReporter logger, - TypeInferenceLogger typeInfLogger) { - - TypeSystem typeSystem = TYPE_SYSTEMS.computeIfAbsent(classLoader, TypeSystem::usingClassLoaderClasspath); - return new JavaAstProcessor( - typeSystem, - typeSystem.bootstrapResolver(), - logger, - typeInfLogger, - languageVersion - ); - } - - - public static JavaAstProcessor create(ClassLoader classLoader, - LanguageVersion languageVersion, - SemanticErrorReporter logger) { - return create(classLoader, languageVersion, logger, defaultTypeInfLogger()); - } - public static JavaAstProcessor create(JavaLanguageProcessor globalProcessor, - SemanticErrorReporter semanticErrorReporter) { - return create(globalProcessor.getProperties().getAnalysisClassLoader(), - globalProcessor.getLanguageVersion(), - semanticErrorReporter, - defaultTypeInfLogger()); - } - - public static JavaAstProcessor create(TypeSystem typeSystem, - LanguageVersion languageVersion, - SemanticErrorReporter semanticLogger, + SemanticErrorReporter semanticErrorReporter, TypeInferenceLogger typeInfLogger) { + + + TypeSystem typeSystem1 = globalProcessor.getTypeSystem(); return new JavaAstProcessor( - typeSystem, - typeSystem.bootstrapResolver(), - semanticLogger, + typeSystem1, + typeSystem1.bootstrapResolver(), + semanticErrorReporter, typeInfLogger, - languageVersion + globalProcessor.getLanguageVersion() ); } + public static JavaAstProcessor create(JavaLanguageProcessor globalProcessor, SemanticErrorReporter reporter) { + return create(globalProcessor, reporter, globalProcessor.newTypeInfLogger()); + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageProcessor.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageProcessor.java index 2f52880f14..0f9d524430 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageProcessor.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/internal/JavaLanguageProcessor.java @@ -4,6 +4,8 @@ package net.sourceforge.pmd.lang.java.internal; +import java.util.Objects; + import net.sourceforge.pmd.lang.BatchLanguageProcessor; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.LanguageVersionHandler; @@ -13,6 +15,7 @@ import net.sourceforge.pmd.lang.java.ast.JavaParser; import net.sourceforge.pmd.lang.java.ast.internal.LanguageLevelChecker; import net.sourceforge.pmd.lang.java.ast.internal.ReportingStrategy; import net.sourceforge.pmd.lang.java.internal.JavaLanguageHandler.JavaMetricsProvider; +import net.sourceforge.pmd.lang.java.internal.JavaLanguageProperties.InferenceLoggingVerbosity; import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleViolationFactory; import net.sourceforge.pmd.lang.java.rule.xpath.internal.BaseContextNodeTestFun; import net.sourceforge.pmd.lang.java.rule.xpath.internal.GetCommentOnFunction; @@ -20,6 +23,10 @@ import net.sourceforge.pmd.lang.java.rule.xpath.internal.GetModifiersFun; import net.sourceforge.pmd.lang.java.rule.xpath.internal.MatchesSignatureFunction; import net.sourceforge.pmd.lang.java.rule.xpath.internal.MetricFunction; import net.sourceforge.pmd.lang.java.rule.xpath.internal.NodeIsFunction; +import net.sourceforge.pmd.lang.java.types.TypeSystem; +import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger; +import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger.SimpleLogger; +import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger.VerboseLogger; import net.sourceforge.pmd.lang.metrics.LanguageMetricsProvider; import net.sourceforge.pmd.lang.rule.RuleViolationFactory; import net.sourceforge.pmd.lang.rule.xpath.impl.XPathHandler; @@ -33,9 +40,12 @@ public class JavaLanguageProcessor extends BatchLanguageProcessor levelChecker = new LanguageLevelChecker<>(properties.getInternalJdkVersion(), @@ -45,6 +55,12 @@ public class JavaLanguageProcessor extends BatchLanguageProcessor INTERNAL_INFERENCE_LOGGING_VERBOSITY = + PropertyFactory.enumProperty("xTypeInferenceLogging", + EnumUtils.getEnumMap(InferenceLoggingVerbosity.class)) + .desc("Verbosity of the type inference logging") + .defaultValue(InferenceLoggingVerbosity.DISABLED) + .build(); + public JavaLanguageProperties() { super(JavaLanguageModule.getInstance()); + definePropertyDescriptor(INTERNAL_INFERENCE_LOGGING_VERBOSITY); } boolean isPreviewEnabled() { @@ -25,13 +37,17 @@ public class JavaLanguageProperties extends JvmLanguagePropertyBundle { // Todo that's ugly.. LanguageVersion version = getLanguageVersion(); String verString = version.getVersion(); - if (isPreviewEnabled()){ + if (isPreviewEnabled()) { verString = verString.substring(0, verString.length() - "-preview".length()); } - if (verString.startsWith("1.")) + if (verString.startsWith("1.")) { verString = verString.substring(2); + } return Integer.parseInt(verString); } + public enum InferenceLoggingVerbosity { + DISABLED, SIMPLE, VERBOSE + } } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/TypeInferenceLogger.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/TypeInferenceLogger.java index 70bef077a7..8c29c626cb 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/TypeInferenceLogger.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/TypeInferenceLogger.java @@ -104,6 +104,13 @@ public interface TypeInferenceLogger { return false; } + /** + * Return an instance for concurrent use in another thread. + * If this is Noop, then return the same instance because it's + * thread-safe. + */ + TypeInferenceLogger newInstance(); + static TypeInferenceLogger noop() { return SimpleLogger.NOOP; } @@ -116,10 +123,15 @@ public interface TypeInferenceLogger { public boolean isNoop() { return true; } + + @Override + public TypeInferenceLogger newInstance() { + return this; + } }; - private final PrintStream out; + protected final PrintStream out; protected static final int LEVEL_INCREMENT = 4; private int level; private String indent; @@ -307,6 +319,10 @@ public interface TypeInferenceLogger { return ivar + kind.getSym() + colorIvars(colorPunct(bound)); } + @Override + public TypeInferenceLogger newInstance() { + return new SimpleLogger(out); + } } /** @@ -437,6 +453,11 @@ public interface TypeInferenceLogger { println("Failed: " + exception.getReason()); } + @Override + public TypeInferenceLogger newInstance() { + return new VerboseLogger(out); + } + } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java index 0643500672..d3c0853600 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/JavaParsingHelper.java @@ -26,7 +26,7 @@ import net.sourceforge.pmd.lang.ast.test.BaseParsingHelper; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.JavaParser; import net.sourceforge.pmd.lang.java.internal.JavaAstProcessor; -import net.sourceforge.pmd.lang.java.internal.JavaLanguageHandler; +import net.sourceforge.pmd.lang.java.internal.JavaLanguageProcessor; import net.sourceforge.pmd.lang.java.types.TypeSystem; import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger; import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger.SimpleLogger; @@ -66,12 +66,14 @@ public class JavaParsingHelper extends BaseParsingHelper JSymbolTable.shouldResolveVarTo(simpleName: String, expected: JVariableSymbol): T { val resolved = variables().resolveFirst(simpleName) ?: throw AssertionError("Unresolved variable $simpleName, expected $expected") diff --git a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt index 82bea03f7e..0cca5eafc0 100644 --- a/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt +++ b/pmd-lang-test/src/main/kotlin/net/sourceforge/pmd/lang/ast/test/BaseParsingHelper.kt @@ -5,10 +5,13 @@ package net.sourceforge.pmd.lang.ast.test import net.sourceforge.pmd.* import net.sourceforge.pmd.lang.Language +import net.sourceforge.pmd.lang.LanguageProcessor +import net.sourceforge.pmd.lang.LanguagePropertyBundle import net.sourceforge.pmd.lang.LanguageRegistry import net.sourceforge.pmd.lang.LanguageVersion import net.sourceforge.pmd.lang.LanguageVersionHandler import net.sourceforge.pmd.lang.ast.* +import net.sourceforge.pmd.lang.ast.Parser.ParserTask import net.sourceforge.pmd.lang.document.TextDocument import net.sourceforge.pmd.lang.document.TextFile import net.sourceforge.pmd.lang.rule.XPathRule @@ -113,9 +116,6 @@ abstract class BaseParsingHelper, T : RootNode return getVersion(version).languageVersionHandler } - val defaultHandler: LanguageVersionHandler - get() = defaultVersion.languageVersionHandler - @JvmOverloads fun getNodes(target: Class, source: String, version: String? = null): List = @@ -127,22 +127,38 @@ abstract class BaseParsingHelper, T : RootNode * so. */ @JvmOverloads - open fun parse( + fun parse( sourceCode: String, version: String? = null, fileName: String = TextFile.UNKNOWN_FILENAME ): T { val lversion = if (version == null) defaultVersion else getVersion(version) val textDoc = TextDocument.readOnlyString(sourceCode, fileName, lversion) - val task = Parser.ParserTask(textDoc, SemanticErrorReporter.noop()) - return doParse(params, task) + val task = ParserTask(textDoc, SemanticErrorReporter.noop()) + return doParse( + // params and task must match + params.copy(defaultVerString = lversion.version), + task + ) } - protected open fun doParse(params: Params, task: Parser.ParserTask): T { - val parser = task.languageVersion.languageVersionHandler.parser - return rootClass.cast(parser.parse(task)) + protected open fun doParse(params: Params, task: ParserTask): T { + val processor = newProcessor(params) + val root = parseImpl(params, processor, task) + return rootClass.cast(root) } + fun newProcessor(params: Params): LanguageProcessor { + val props = language.newPropertyBundle().apply { + setLanguageVersion(params.defaultVerString) + setProperty(LanguagePropertyBundle.SUPPRESS_MARKER, params.suppressMarker) + } + return language.createProcessor(props) + } + + protected open fun parseImpl(params: Params, processor: LanguageProcessor, task: ParserTask): RootNode = + processor.services().parser.parse(task) + /** * Fetches and [parse]s the [resource] using the context defined for this * instance (by default uses this class' classloader, but can be configured @@ -222,21 +238,15 @@ abstract class BaseParsingHelper, T : RootNode val config = PMDConfiguration().apply { suppressMarker = params.suppressMarker setDefaultLanguageVersion(defaultVersion) + isIgnoreIncrementalAnalysis = true } - val reportBuilder = Report.GlobalReportBuilderListener() - val fullListener = GlobalAnalysisListener.tee(listOf(GlobalAnalysisListener.exceptionThrower(), reportBuilder)) - - - AbstractPMDProcessor.runSingleFile( - listOf(RuleSet.forSingleRule(rule)), - TextFile.forCharSeq(code, fileName, defaultVersion), - fullListener, - config - ) - - fullListener.close() - return reportBuilder.result + return PmdAnalysis.create(config).use { pmd -> + pmd.addListener(GlobalAnalysisListener.exceptionThrower()) + pmd.addRuleSet(RuleSet.forSingleRule(rule)) + pmd.files().addSourceFile(fileName, code) + pmd.performAnalysisAndCollectReport() + } } fun executeRuleOnResource(rule: Rule, resourcePath: String): Report = diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java b/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java index f5938c7454..3f8211a53c 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/test/lang/DummyLanguageModule.java @@ -8,13 +8,10 @@ import net.sourceforge.pmd.annotation.InternalApi; import net.sourceforge.pmd.lang.AbstractPmdLanguageVersionHandler; import net.sourceforge.pmd.lang.BaseLanguageModule; import net.sourceforge.pmd.lang.LanguageRegistry; -import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.ast.AstInfo; import net.sourceforge.pmd.lang.ast.Parser; import net.sourceforge.pmd.lang.ast.Parser.ParserTask; import net.sourceforge.pmd.lang.ast.RootNode; -import net.sourceforge.pmd.lang.ast.SemanticErrorReporter; -import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.document.TextRegion; import net.sourceforge.pmd.lang.rule.impl.DefaultRuleViolationFactory; import net.sourceforge.pmd.test.lang.ast.DummyNode; @@ -49,15 +46,6 @@ public class DummyLanguageModule extends BaseLanguageModule { return (DummyLanguageModule) LanguageRegistry.PMD.getLanguageByFullName(NAME); } - public static DummyRootNode parse(String code, String filename) { - LanguageVersion version = DummyLanguageModule.getInstance().getDefaultVersion(); - ParserTask task = new ParserTask( - TextDocument.readOnlyString(code, filename, version), - SemanticErrorReporter.noop() - ); - return (DummyRootNode) version.getLanguageVersionHandler().getParser().parse(task); - } - public static class Handler extends AbstractPmdLanguageVersionHandler { @Override diff --git a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java index 59012fbc2a..d1b3368861 100644 --- a/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java +++ b/pmd-test/src/main/java/net/sourceforge/pmd/testframework/RuleTst.java @@ -4,8 +4,6 @@ package net.sourceforge.pmd.testframework; -import static net.sourceforge.pmd.util.CollectionUtil.listOf; - import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -43,8 +41,8 @@ import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import net.sourceforge.pmd.PMDConfiguration; +import net.sourceforge.pmd.PmdAnalysis; import net.sourceforge.pmd.Report; -import net.sourceforge.pmd.Report.GlobalReportBuilderListener; import net.sourceforge.pmd.Rule; import net.sourceforge.pmd.RuleSet; import net.sourceforge.pmd.RuleSetLoadException; @@ -54,7 +52,6 @@ import net.sourceforge.pmd.lang.LanguageRegistry; import net.sourceforge.pmd.lang.LanguageVersion; import net.sourceforge.pmd.lang.document.Chars; import net.sourceforge.pmd.lang.document.TextFile; -import net.sourceforge.pmd.processor.AbstractPMDProcessor; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.renderers.TextRenderer; import net.sourceforge.pmd.reporting.GlobalAnalysisListener; @@ -281,53 +278,40 @@ public abstract class RuleTst { } public Report runTestFromString(String code, Rule rule, LanguageVersion languageVersion, boolean isUseAuxClasspath) { - try { - PMDConfiguration configuration = new PMDConfiguration(); - configuration.setIgnoreIncrementalAnalysis(true); - configuration.setDefaultLanguageVersion(languageVersion); - configuration.setThreads(1); + PMDConfiguration configuration = new PMDConfiguration(); + configuration.setIgnoreIncrementalAnalysis(true); + configuration.setDefaultLanguageVersion(languageVersion); + configuration.setThreads(1); + configuration.setIgnoreIncrementalAnalysis(true); - if (isUseAuxClasspath) { - // configure the "auxclasspath" option for unit testing - // we share a single classloader so that pmd-java doesn't create - // a new TypeSystem for every test. This problem will go - // away when languages have a lifecycle. - configuration.setClassLoader(classpathClassLoader); - } else { - // simple class loader, that doesn't delegate to parent. - // this allows us in the tests to simulate PMD run without - // auxclasspath, not even the classes from the test dependencies - // will be found. - configuration.setClassLoader(new ClassLoader() { - @Override - protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { - if (name.startsWith("java.") || name.startsWith("javax.")) { - return super.loadClass(name, resolve); - } - throw new ClassNotFoundException(name); + + if (isUseAuxClasspath) { + // configure the "auxclasspath" option for unit testing + // we share a single classloader so that pmd-java doesn't create + // a new TypeSystem for every test. This problem will go + // away when languages have a lifecycle. + configuration.setClassLoader(classpathClassLoader); + } else { + // simple class loader, that doesn't delegate to parent. + // this allows us in the tests to simulate PMD run without + // auxclasspath, not even the classes from the test dependencies + // will be found. + configuration.setClassLoader(new ClassLoader() { + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (name.startsWith("java.") || name.startsWith("javax.")) { + return super.loadClass(name, resolve); } - }); - } + throw new ClassNotFoundException(name); + } + }); + } - try (GlobalReportBuilderListener reportBuilder = new GlobalReportBuilderListener(); - // Add a listener that throws when an error occurs: - // this replaces ruleContext.setIgnoreExceptions(false) - GlobalAnalysisListener listener = GlobalAnalysisListener.tee(listOf(GlobalAnalysisListener.exceptionThrower(), reportBuilder))) { - - AbstractPMDProcessor.runSingleFile( - listOf(RuleSet.forSingleRule(rule)), - TextFile.forCharSeq(code, "testFile", languageVersion), - listener, - configuration - ); - - listener.close(); - return reportBuilder.getResult(); - } - } catch (RuntimeException e) { - throw e; - } catch (Exception e) { - throw new RuntimeException(e); + try (PmdAnalysis pmd = PmdAnalysis.create(configuration)) { + pmd.files().addFile(TextFile.forCharSeq(code, "testFile", languageVersion)); + pmd.addRuleSet(RuleSet.forSingleRule(rule)); + pmd.addListener(GlobalAnalysisListener.exceptionThrower()); + return pmd.performAnalysisAndCollectReport(); } } diff --git a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java index 54282e2aa8..dcbaec7a3f 100644 --- a/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java +++ b/pmd-test/src/test/java/net/sourceforge/pmd/testframework/RuleTstTest.java @@ -45,8 +45,8 @@ public class RuleTstTest { verify(rule).end(any(RuleContext.class)); verify(rule, atLeastOnce()).getLanguage(); verify(rule, atLeastOnce()).getTargetSelector(); - verify(rule).getMinimumLanguageVersion(); - verify(rule).getMaximumLanguageVersion(); + verify(rule, atLeastOnce()).getMinimumLanguageVersion(); + verify(rule, atLeastOnce()).getMaximumLanguageVersion(); verify(rule).apply(any(Node.class), any(RuleContext.class)); verify(rule, atLeastOnce()).getName(); verify(rule).getPropertiesByPropertyDescriptor(); diff --git a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java index eab303c55d..00ac3cd8be 100644 --- a/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java +++ b/pmd-visualforce/src/main/java/net/sourceforge/pmd/lang/vf/ast/ApexClassPropertyTypes.java @@ -66,6 +66,7 @@ class ApexClassPropertyTypes extends SalesforceFieldTypes { } static Node parseApex(Path apexFilePath) { + // TODO to remove that, we need to make sure there is a started LanguageProcessor for Apex and fetch it. LanguageVersion languageVersion = ApexLanguageModule.getInstance().getDefaultVersion(); try (TextFile file = TextFile.forPath(apexFilePath, StandardCharsets.UTF_8, languageVersion); TextDocument textDocument = TextDocument.create(file)) { diff --git a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/DomXPathRule.java b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/DomXPathRule.java index d94c8df66f..ce2df9d9de 100644 --- a/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/DomXPathRule.java +++ b/pmd-xml/src/main/java/net/sourceforge/pmd/lang/xml/rule/DomXPathRule.java @@ -4,7 +4,10 @@ package net.sourceforge.pmd.lang.xml.rule; +import java.util.Objects; + import net.sourceforge.pmd.RuleContext; +import net.sourceforge.pmd.lang.LanguageProcessor; import net.sourceforge.pmd.lang.ast.Node; import net.sourceforge.pmd.lang.rule.AbstractRule; import net.sourceforge.pmd.lang.rule.XPathRule; @@ -149,14 +152,17 @@ public class DomXPathRule extends AbstractRule { } } + @Override + public void initialize(LanguageProcessor languageProcessor) { + query = new SaxonDomXPathQuery(getProperty(XPATH_EXPR), + getProperty(DEFAULT_NS_URI), + getPropertyDescriptors(), + languageProcessor.services().getXPathHandler()); + + } + private SaxonDomXPathQuery getXPathQuery() { - if (query == null) { - query = new SaxonDomXPathQuery(getProperty(XPATH_EXPR), - getProperty(DEFAULT_NS_URI), - getPropertyDescriptors(), - getLanguage().getDefaultVersion().getLanguageVersionHandler().getXPathHandler()); - } - return query; + return Objects.requireNonNull(query, "rule not initialized"); } }