From 9bf476ac4fc321a7ac59aef65b65b32d56dde110 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Fri, 5 Jul 2024 11:39:25 +0200 Subject: [PATCH 01/58] [core] Don't log warning about skipLexicalErrors twice Fixes #5091 --- docs/pages/release_notes.md | 2 ++ .../main/java/net/sourceforge/pmd/ant/CPDTask.java | 3 --- .../pmd/cli/commands/internal/CpdCommand.java | 4 ---- .../java/net/sourceforge/pmd/cli/BaseCliTest.java | 4 ++-- .../java/net/sourceforge/pmd/cli/CpdCliTest.java | 14 ++++++++++++++ .../net/sourceforge/pmd/cpd/CPDConfiguration.java | 3 ++- .../java/net/sourceforge/pmd/cpd/CpdAnalysis.java | 4 ---- .../net/sourceforge/pmd/cpd/CpdAnalysisTest.java | 2 -- 8 files changed, 20 insertions(+), 16 deletions(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index bc1a36ec7a..24452af994 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -15,6 +15,8 @@ This is a {{ site.pmd.release_type }} release. ### ๐Ÿš€ New and noteworthy ### ๐Ÿ› Fixed Issues +* core + * [#5091](https://github.com/pmd/pmd/issues/5091): \[core] PMD CPD v7.3.0 gives deprecation warning for skipLexicalErrors even when not used ### ๐Ÿšจ API Changes diff --git a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java index c812893930..86558b6048 100644 --- a/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java +++ b/pmd-ant/src/main/java/net/sourceforge/pmd/ant/CPDTask.java @@ -111,9 +111,6 @@ public class CPDTask extends Task { + "Use failOnError=\"false\" to not fail the build.", Project.MSG_WARN); } - // implicitly enable skipLexicalErrors, so that we can fail the build at the end. A report is created in any case. - config.setSkipLexicalErrors(true); - config.setIgnoreAnnotations(ignoreAnnotations); config.setIgnoreLiterals(ignoreLiterals); config.setIgnoreIdentifiers(ignoreIdentifiers); diff --git a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java index fe8a835774..f823aa3ab9 100644 --- a/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java +++ b/pmd-cli/src/main/java/net/sourceforge/pmd/cli/commands/internal/CpdCommand.java @@ -124,7 +124,6 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand configuration.setRendererName(rendererName); configuration.setSkipBlocksPattern(skipBlocksPattern); configuration.setSkipDuplicates(skipDuplicates); - configuration.setSkipLexicalErrors(skipLexicalErrors); configuration.setSourceEncoding(encoding.getEncoding()); configuration.setInputUri(uri); @@ -133,9 +132,6 @@ public class CpdCommand extends AbstractAnalysisPmdSubcommand configuration.setFailOnError(false); } - // implicitly enable skipLexicalErrors, so that we can fail the build at the end. A report is created in any case. - configuration.setSkipLexicalErrors(true); - return configuration; } diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/BaseCliTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/BaseCliTest.java index 33123c3ddd..c19230a94f 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/BaseCliTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/BaseCliTest.java @@ -5,7 +5,7 @@ package net.sourceforge.pmd.cli; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.emptyString; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.ByteArrayOutputStream; @@ -146,7 +146,7 @@ abstract class BaseCliTest { } public void checkNoErrorOutput() { - checkStdErr(equalTo("")); + checkStdErr(emptyString()); } public void checkStdOut(Matcher matcher) { diff --git a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java index 3ff31a255d..acbdc1db6f 100644 --- a/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java +++ b/pmd-cli/src/test/java/net/sourceforge/pmd/cli/CpdCliTest.java @@ -239,10 +239,24 @@ class CpdCliTest extends BaseCliTest { "--skip-lexical-errors") .verify(r -> { r.checkStdErr(containsPattern("Skipping file: Lexical error in file .*?BadFile\\.java")); + r.checkStdErr(containsString("--skip-lexical-errors is deprecated. Use --no-fail-on-error instead.")); r.checkStdOut(containsString("Found a 5 line (13 tokens) duplication")); }); } + /** + * @see [core] PMD CPD v7.3.0 gives deprecation warning for skipLexicalErrors even when not used #5091 + * @throws Exception + */ + @Test + void noWarningsWithoutSkipLexicalErrors() throws Exception { + runCliSuccessfully("--minimum-tokens", "340", "--language", "java", "--dir", SRC_DIR, "--format", "text") + .verify(r -> { + r.checkNoErrorOutput(); + r.checkStdOut(emptyString()); + }); + } + @Test void testExitCodeWithLexicalErrors() throws Exception { runCli(RECOVERED_ERRORS_OR_VIOLATIONS, diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java index 3826781b86..8c63ae3252 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CPDConfiguration.java @@ -67,7 +67,8 @@ public class CPDConfiguration extends AbstractConfiguration { private boolean ignoreIdentifierAndLiteralSequences = false; @Deprecated - private boolean skipLexicalErrors = false; + // Note: The default value was false until up to 7.3.0 and is true since 7.4.0 + private boolean skipLexicalErrors = true; private boolean noSkipBlocks = false; diff --git a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java index 1df33f5274..6117998e4a 100644 --- a/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java +++ b/pmd-core/src/main/java/net/sourceforge/pmd/cpd/CpdAnalysis.java @@ -153,10 +153,6 @@ public final class CpdAnalysis implements AutoCloseable { @SuppressWarnings("PMD.CloseResource") public void performAnalysis(Consumer consumer) { - if (configuration.isSkipLexicalErrors()) { - LOGGER.warn("The option skipLexicalErrors is deprecated. Use failOnError instead."); - } - try (SourceManager sourceManager = new SourceManager(files.getCollectedFiles())) { Map tokenizers = sourceManager.getTextFiles().stream() diff --git a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java index e5140e9abe..0238b76fa3 100644 --- a/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java +++ b/pmd-core/src/test/java/net/sourceforge/pmd/cpd/CpdAnalysisTest.java @@ -223,7 +223,6 @@ class CpdAnalysisTest { PmdReporter reporter = mock(PmdReporter.class); config.setReporter(reporter); - config.setSkipLexicalErrors(true); // must be true, otherwise CPD is aborted with first processing error try (CpdAnalysis cpd = CpdAnalysis.create(config)) { assertTrue(cpd.files().addSourceFile(FileId.fromPathLikeString("foo.dummy"), DummyLanguageModule.CPD_THROW_LEX_EXCEPTION)); assertTrue(cpd.files().addSourceFile(FileId.fromPathLikeString("foo2.dummy"), DummyLanguageModule.CPD_THROW_MALFORMED_SOURCE_EXCEPTION)); @@ -252,7 +251,6 @@ class CpdAnalysisTest { PmdReporter reporter = mock(PmdReporter.class); config.setReporter(reporter); - config.setSkipLexicalErrors(true); try (CpdAnalysis cpd = CpdAnalysis.create(config)) { assertTrue(cpd.files().addSourceFile(FileId.fromPathLikeString("foo.dummy"), DummyLanguageModule.CPD_THROW_LEX_EXCEPTION)); assertTrue(cpd.files().addSourceFile(FileId.fromPathLikeString("foo2.dummy"), DummyLanguageModule.CPD_THROW_MALFORMED_SOURCE_EXCEPTION)); From c053f00fb5f9895995b126ccabe5828fcb364d38 Mon Sep 17 00:00:00 2001 From: Krzysztof Debski Date: Thu, 11 Jul 2024 16:54:38 +0200 Subject: [PATCH 02/58] [java] Do not report UnusedPrivateMethod for method referenced by @lombok.Builder.ObtainVia --- .../UnusedPrivateMethodRule.java | 42 ++++++++++++------- .../bestpractices/xml/UnusedPrivateMethod.xml | 22 ++++++++++ 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java index 4d239378b9..e91a9a5477 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java @@ -17,6 +17,7 @@ import java.util.stream.Stream; import net.sourceforge.pmd.lang.ast.NodeStream; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; +import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodReference; @@ -29,6 +30,7 @@ import net.sourceforge.pmd.lang.java.rule.internal.AbstractIgnoredAnnotationRule import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; import net.sourceforge.pmd.util.CollectionUtil; +import org.apache.commons.lang3.StringUtils; /** * This rule detects private methods, that are not used and can therefore be @@ -46,24 +48,36 @@ public class UnusedPrivateMethodRule extends AbstractIgnoredAnnotationRule { @Override public Object visit(ASTCompilationUnit file, Object param) { - // We do three traversals: - // - one to find methods referenced by Junit5 MethodSource annotations + // We do four traversals: + // - one to find methods referenced by Junit5 MethodSource + // - one to find methods referenced by Lombok Builder.ObtainVia // - one to find the "interesting methods", ie those that may be violations // - another to find the possible usages. We only try to resolve // method calls/method refs that may refer to a method in the // first set, ie, not every call in the file. - - Set methodsUsedByAnnotations = file.descendants(ASTMethodDeclaration.class) - .crossFindBoundaries() - .children(ASTModifierList.class) - .children(ASTAnnotation.class) - .filter(t -> TypeTestUtil.isA("org.junit.jupiter.params.provider.MethodSource", t)) - .toStream() - // Get the referenced method namesโ€ฆ if none, use the test method name instead - .flatMap(a -> a.getFlatValue("value").isEmpty() - ? Stream.of(a.ancestors(ASTMethodDeclaration.class).first().getName()) - : a.getFlatValue("value").toStream().map(mv -> (String) mv.getConstValue())) - .collect(Collectors.toSet()); + Set methodsUsedByAnnotations = + Stream.concat( + file.descendants(ASTMethodDeclaration.class) + .crossFindBoundaries() + .children(ASTModifierList.class) + .children(ASTAnnotation.class) + .filter(t -> TypeTestUtil.isA("org.junit.jupiter.params.provider.MethodSource", t)) + .toStream() + // Get the referenced method namesโ€ฆ if none, use the test method name instead + .flatMap(a -> a.getFlatValue("value").isEmpty() + ? Stream.of(a.ancestors(ASTMethodDeclaration.class).first().getName()) + : a.getFlatValue("value").toStream().map(mv -> (String) mv.getConstValue())), + file.descendants(ASTFieldDeclaration.class) + .crossFindBoundaries() + .children(ASTModifierList.class) + .children(ASTAnnotation.class) + .filter(t -> TypeTestUtil.isA("lombok.Builder.ObtainVia", t)) + .toStream() + .flatMap(a -> a.getFlatValue("method").toStream() + .map(mv -> (String) mv.getConstValue()) + .filter(StringUtils::isNotEmpty)) + ) + .collect(Collectors.toSet()); Map> consideredNames = file.descendants(ASTMethodDeclaration.class) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml index 5f2871d3dc..68747b49b3 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml @@ -2074,4 +2074,26 @@ class FooTest{ } ]]> + + UnusedPrivateMethod for Lombok ObtainVia #5110 + 0 + foo; + + private List fooProvider() { + return Collections.emptyList(); + } +} + ]]> + From 45d68cf455741a815b702936c5a2f779bfcabc61 Mon Sep 17 00:00:00 2001 From: Krzysztof Debski Date: Thu, 11 Jul 2024 17:33:39 +0200 Subject: [PATCH 03/58] [java] Imports order fix --- .../lang/java/rule/bestpractices/UnusedPrivateMethodRule.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java index e91a9a5477..445061e2fc 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java @@ -14,6 +14,8 @@ import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.commons.lang3.StringUtils; + import net.sourceforge.pmd.lang.ast.NodeStream; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; @@ -30,7 +32,6 @@ import net.sourceforge.pmd.lang.java.rule.internal.AbstractIgnoredAnnotationRule import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; import net.sourceforge.pmd.util.CollectionUtil; -import org.apache.commons.lang3.StringUtils; /** * This rule detects private methods, that are not used and can therefore be From cb1d1963a42582ac4fc4006fc9abab10bec280dc Mon Sep 17 00:00:00 2001 From: Krzysztof Debski Date: Wed, 17 Jul 2024 10:18:31 +0200 Subject: [PATCH 04/58] [java] Ignore unused private methods referenced by any field annotation attribute --- .../pmd/lang/java/ast/ASTAnnotation.java | 25 +++++++++++++++++-- .../UnusedPrivateMethodRule.java | 10 +++++--- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java index b3981ea8ad..a0e6407546 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java @@ -81,8 +81,29 @@ public final class ASTAnnotation extends AbstractJavaTypeNode implements ASTMemb */ public NodeStream getFlatValue(String attrName) { return NodeStream.of(getAttribute(attrName)) - .flatMap(v -> v instanceof ASTMemberValueArrayInitializer ? v.children(ASTMemberValue.class) - : NodeStream.of(v)); + .flatMap(this::flatValue); + } + + /** + * Return expression values for all attributes. + * This may flatten an array initializer. For example, for the attribute + * named "value": + *
{@code
+     * - @SuppressWarnings -> returns empty node stream
+     * - @SuppressWarning("fallthrough") -> returns ["fallthrough"]
+     * - @SuppressWarning(value={"fallthrough"}) -> returns ["fallthrough"]
+     * - @SuppressWarning({"fallthrough", "rawtypes"}) -> returns ["fallthrough", "rawtypes"]
+     * }
+ */ + public NodeStream getFlatValues() { + return getMembers().map(ASTMemberValuePair::getValue) + .flatMap(this::flatValue); + } + + private NodeStream flatValue(ASTMemberValue value) { + return value instanceof ASTMemberValueArrayInitializer + ? value.children(ASTMemberValue.class) + : NodeStream.of(value); } /** diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java index 445061e2fc..0c7493b909 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java @@ -20,6 +20,7 @@ import net.sourceforge.pmd.lang.ast.NodeStream; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; +import net.sourceforge.pmd.lang.java.ast.ASTMemberValue; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodReference; @@ -51,7 +52,7 @@ public class UnusedPrivateMethodRule extends AbstractIgnoredAnnotationRule { public Object visit(ASTCompilationUnit file, Object param) { // We do four traversals: // - one to find methods referenced by Junit5 MethodSource - // - one to find methods referenced by Lombok Builder.ObtainVia + // - one to find methods referenced by any String attribute of any field annotation, for example Lombok Builder.ObtainVia // - one to find the "interesting methods", ie those that may be violations // - another to find the possible usages. We only try to resolve // method calls/method refs that may refer to a method in the @@ -72,10 +73,11 @@ public class UnusedPrivateMethodRule extends AbstractIgnoredAnnotationRule { .crossFindBoundaries() .children(ASTModifierList.class) .children(ASTAnnotation.class) - .filter(t -> TypeTestUtil.isA("lombok.Builder.ObtainVia", t)) .toStream() - .flatMap(a -> a.getFlatValue("method").toStream() - .map(mv -> (String) mv.getConstValue()) + .flatMap(a -> a.getFlatValues().toStream() + .map(ASTMemberValue::getConstValue) + .filter(value -> value instanceof String) + .map(value -> (String) value) .filter(StringUtils::isNotEmpty)) ) .collect(Collectors.toSet()); From 9006dd1a89931c210f9524cbdbd4b5e6f4242bf3 Mon Sep 17 00:00:00 2001 From: Krzysztof Debski Date: Mon, 22 Jul 2024 13:22:24 +0200 Subject: [PATCH 05/58] [java] Generalize annotation handling in UnusedPrivateMethodRule --- .../UnusedPrivateMethodRule.java | 53 +++++++++---------- .../bestpractices/xml/UnusedPrivateMethod.xml | 12 +++++ 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java index 0c7493b909..4d73a7acc7 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java @@ -10,6 +10,7 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -17,14 +18,13 @@ import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.lang.ast.NodeStream; +import net.sourceforge.pmd.lang.ast.impl.GenericNode; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; -import net.sourceforge.pmd.lang.java.ast.ASTFieldDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMemberValue; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTMethodReference; -import net.sourceforge.pmd.lang.java.ast.ASTModifierList; import net.sourceforge.pmd.lang.java.ast.JavaNode; import net.sourceforge.pmd.lang.java.ast.MethodUsage; import net.sourceforge.pmd.lang.java.ast.ModifierOwner.Visibility; @@ -50,37 +50,34 @@ public class UnusedPrivateMethodRule extends AbstractIgnoredAnnotationRule { @Override public Object visit(ASTCompilationUnit file, Object param) { - // We do four traversals: - // - one to find methods referenced by Junit5 MethodSource - // - one to find methods referenced by any String attribute of any field annotation, for example Lombok Builder.ObtainVia + // We do three traversals: + // - one to find methods: + // --- referenced by any attribute of any annotation + // --- with name same as a method annotated with Junit5 MethodSource if the annotation value is empty // - one to find the "interesting methods", ie those that may be violations // - another to find the possible usages. We only try to resolve // method calls/method refs that may refer to a method in the // first set, ie, not every call in the file. Set methodsUsedByAnnotations = - Stream.concat( - file.descendants(ASTMethodDeclaration.class) - .crossFindBoundaries() - .children(ASTModifierList.class) - .children(ASTAnnotation.class) - .filter(t -> TypeTestUtil.isA("org.junit.jupiter.params.provider.MethodSource", t)) - .toStream() - // Get the referenced method namesโ€ฆ if none, use the test method name instead - .flatMap(a -> a.getFlatValue("value").isEmpty() - ? Stream.of(a.ancestors(ASTMethodDeclaration.class).first().getName()) - : a.getFlatValue("value").toStream().map(mv -> (String) mv.getConstValue())), - file.descendants(ASTFieldDeclaration.class) - .crossFindBoundaries() - .children(ASTModifierList.class) - .children(ASTAnnotation.class) - .toStream() - .flatMap(a -> a.getFlatValues().toStream() - .map(ASTMemberValue::getConstValue) - .filter(value -> value instanceof String) - .map(value -> (String) value) - .filter(StringUtils::isNotEmpty)) - ) - .collect(Collectors.toSet()); + file.descendants(ASTAnnotation.class) + .crossFindBoundaries() + .toStream() + .flatMap(a -> Stream.concat( + a.getFlatValues().toStream() + .map(ASTMemberValue::getConstValue) + .filter(value -> value instanceof String) + .map(value -> (String) value) + .filter(StringUtils::isNotEmpty), + TypeTestUtil.isA("org.junit.jupiter.params.provider.MethodSource", a) && a.getFlatValue("value").isEmpty() + ? Optional.ofNullable(a.getParent()) + .map(GenericNode::getParent) + .filter(m -> m instanceof ASTMethodDeclaration) + .map(m -> ((ASTMethodDeclaration) m).getName()) + .map(Stream::of) + .orElseGet(Stream::empty) + : Stream.empty()) + ) + .collect(Collectors.toSet()); Map> consideredNames = file.descendants(ASTMethodDeclaration.class) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml index 68747b49b3..2b597b3abf 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/UnusedPrivateMethod.xml @@ -27,6 +27,18 @@ public class Foo { ]]> + + simple unused annotated private method + 1 + + + anonymous inner class calls private method 0 From 121cdba3a4f4e2eaa60b7f571d5872c1e905482e Mon Sep 17 00:00:00 2001 From: Krzysztof Debski Date: Mon, 22 Jul 2024 13:27:04 +0200 Subject: [PATCH 06/58] [java] Make 'ASTAnnotation#flatValue' method static --- .../net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java index a0e6407546..d69c0e3ac5 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ASTAnnotation.java @@ -81,7 +81,7 @@ public final class ASTAnnotation extends AbstractJavaTypeNode implements ASTMemb */ public NodeStream getFlatValue(String attrName) { return NodeStream.of(getAttribute(attrName)) - .flatMap(this::flatValue); + .flatMap(ASTAnnotation::flatValue); } /** @@ -97,10 +97,10 @@ public final class ASTAnnotation extends AbstractJavaTypeNode implements ASTMemb */ public NodeStream getFlatValues() { return getMembers().map(ASTMemberValuePair::getValue) - .flatMap(this::flatValue); + .flatMap(ASTAnnotation::flatValue); } - private NodeStream flatValue(ASTMemberValue value) { + private static NodeStream flatValue(ASTMemberValue value) { return value instanceof ASTMemberValueArrayInitializer ? value.children(ASTMemberValue.class) : NodeStream.of(value); From 57e8954556588b01808c0d0d88c9baeec1f91af4 Mon Sep 17 00:00:00 2001 From: Krzysztof Debski Date: Mon, 22 Jul 2024 17:33:45 +0200 Subject: [PATCH 07/58] [java] Simplify processing of MethodSource annotation --- .../bestpractices/UnusedPrivateMethodRule.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java index 4d73a7acc7..399456c39d 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java @@ -10,7 +10,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -18,7 +17,6 @@ import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import net.sourceforge.pmd.lang.ast.NodeStream; -import net.sourceforge.pmd.lang.ast.impl.GenericNode; import net.sourceforge.pmd.lang.java.ast.ASTAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTCompilationUnit; import net.sourceforge.pmd.lang.java.ast.ASTMemberValue; @@ -68,14 +66,13 @@ public class UnusedPrivateMethodRule extends AbstractIgnoredAnnotationRule { .filter(value -> value instanceof String) .map(value -> (String) value) .filter(StringUtils::isNotEmpty), - TypeTestUtil.isA("org.junit.jupiter.params.provider.MethodSource", a) && a.getFlatValue("value").isEmpty() - ? Optional.ofNullable(a.getParent()) - .map(GenericNode::getParent) - .filter(m -> m instanceof ASTMethodDeclaration) - .map(m -> ((ASTMethodDeclaration) m).getName()) - .map(Stream::of) - .orElseGet(Stream::empty) - : Stream.empty()) + NodeStream.of(a) + .filter(it -> TypeTestUtil.isA("org.junit.jupiter.params.provider.MethodSource", it) + && it.getFlatValue("value").isEmpty()) + .parents().parents() + .filterIs(ASTMethodDeclaration.class) + .toStream() + .map(ASTMethodDeclaration::getName)) ) .collect(Collectors.toSet()); From 607684c6cb60403c99e1b6e009f089552aab9a63 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Tue, 23 Jul 2024 19:36:53 +0200 Subject: [PATCH 08/58] Update gems Fixes https://github.com/pmd/pmd/security/dependabot/58 Fixes https://github.com/pmd/pmd/security/dependabot/59 Fixes CVE-2024-39908 Fixes https://github.com/advisories/GHSA-4xqq-m2hx-25v8 --- Gemfile.lock | 26 +++++++++++++------------- docs/Gemfile.lock | 26 ++++++++++++++------------ 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 858d9626a0..f8b1e9cb86 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,8 @@ GEM remote: https://rubygems.org/ specs: - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) - base64 (0.2.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) bigdecimal (3.1.8) claide (1.1.0) claide-plugins (0.9.2) @@ -11,7 +10,7 @@ GEM nap open4 (~> 1.3) colored2 (3.1.2) - concurrent-ruby (1.2.3) + concurrent-ruby (1.3.3) cork (0.3.0) colored2 (~> 3.1) danger (9.4.3) @@ -30,8 +29,9 @@ GEM differ (0.1.2) et-orbi (1.2.11) tzinfo - faraday (2.9.0) + faraday (2.10.0) faraday-net_http (>= 2.0, < 3.2) + logger faraday-http-cache (2.5.1) faraday (>= 0.8) faraday-net_http (3.1.0) @@ -46,16 +46,16 @@ GEM rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) - liquid (5.5.0) + liquid (5.5.1) + logger (1.6.0) logger-colors (1.0.0) nap (1.1.0) net-http (0.4.1) uri no_proxy_fix (0.1.2) - nokogiri (1.16.5-x86_64-linux) + nokogiri (1.16.6-x86_64-linux) racc (~> 1.4) - octokit (8.1.0) - base64 + octokit (9.1.0) faraday (>= 1, < 3) sawyer (~> 0.9) open4 (1.3.4) @@ -66,13 +66,13 @@ GEM nokogiri (~> 1.13) rufus-scheduler (~> 3.8) slop (~> 4.9) - public_suffix (5.0.5) + public_suffix (6.0.1) raabro (1.4.0) racc (1.8.0) rchardet (1.8.0) - rexml (3.2.8) - strscan (>= 3.0.9) - rouge (4.2.1) + rexml (3.3.2) + strscan + rouge (4.3.0) rufus-scheduler (3.9.1) fugit (~> 1.1, >= 1.1.6) safe_yaml (1.0.5) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index bd44b12521..ffb75f2b77 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.1.3.3) + activesupport (7.1.3.4) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.0.2) @@ -11,8 +11,8 @@ GEM minitest (>= 5.1) mutex_m tzinfo (~> 2.0) - addressable (2.8.6) - public_suffix (>= 2.0.2, < 6.0) + addressable (2.8.7) + public_suffix (>= 2.0.2, < 7.0) base64 (0.2.0) bigdecimal (3.1.8) coffee-script (2.4.1) @@ -21,10 +21,10 @@ GEM coffee-script-source (1.12.2) colorator (1.1.0) commonmarker (0.23.10) - concurrent-ruby (1.2.3) + concurrent-ruby (1.3.3) connection_pool (2.4.1) csv (3.3.0) - dnsruby (1.72.1) + dnsruby (1.72.2) simpleidn (~> 0.2.1) drb (2.2.1) em-websocket (0.5.3) @@ -34,11 +34,12 @@ GEM ffi (>= 1.15.0) eventmachine (1.2.7) execjs (2.9.1) - faraday (2.9.0) + faraday (2.10.0) faraday-net_http (>= 2.0, < 3.2) + logger faraday-net_http (3.1.0) net-http - ffi (1.16.3) + ffi (1.17.0-x86_64-linux-gnu) forwardable-extended (2.6.0) gemoji (4.1.0) github-pages (231) @@ -214,29 +215,30 @@ GEM listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) + logger (1.6.0) mercenary (0.3.6) minima (2.5.1) jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.23.1) + minitest (5.24.1) mutex_m (0.2.0) net-http (0.4.1) uri - nokogiri (1.16.5-x86_64-linux) + nokogiri (1.16.6-x86_64-linux) racc (~> 1.4) octokit (4.25.1) faraday (>= 1, < 3) sawyer (~> 0.9) pathutil (0.16.2) forwardable-extended (~> 2.6) - public_suffix (5.0.5) + public_suffix (5.1.1) racc (1.8.0) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rexml (3.2.8) - strscan (>= 3.0.9) + rexml (3.3.2) + strscan rouge (3.30.0) rubyzip (2.3.2) safe_yaml (1.0.5) From e8bfc0d01cebf1bb89c5dc9fa400f6e2e40c5be2 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Tue, 23 Jul 2024 20:05:11 +0200 Subject: [PATCH 09/58] [java] UnusedPrivateMethodRule - fixups from PR review --- .../UnusedPrivateMethodRule.java | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java index 399456c39d..1552bdfa44 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java @@ -57,24 +57,26 @@ public class UnusedPrivateMethodRule extends AbstractIgnoredAnnotationRule { // method calls/method refs that may refer to a method in the // first set, ie, not every call in the file. Set methodsUsedByAnnotations = - file.descendants(ASTAnnotation.class) - .crossFindBoundaries() - .toStream() - .flatMap(a -> Stream.concat( - a.getFlatValues().toStream() - .map(ASTMemberValue::getConstValue) - .filter(value -> value instanceof String) - .map(value -> (String) value) - .filter(StringUtils::isNotEmpty), - NodeStream.of(a) - .filter(it -> TypeTestUtil.isA("org.junit.jupiter.params.provider.MethodSource", it) - && it.getFlatValue("value").isEmpty()) - .parents().parents() - .filterIs(ASTMethodDeclaration.class) - .toStream() - .map(ASTMethodDeclaration::getName)) - ) - .collect(Collectors.toSet()); + file.descendants(ASTAnnotation.class) + .crossFindBoundaries() + .toStream() + .flatMap(a -> Stream.concat( + a.getFlatValues().toStream() + .map(ASTMemberValue::getConstValue) + .filter(String.class::isInstance) + .map(String.class::cast) + .filter(StringUtils::isNotEmpty), + NodeStream.of(a) + .filter(it -> TypeTestUtil.isA("org.junit.jupiter.params.provider.MethodSource", it) + && it.getFlatValue("value").isEmpty()) + .ancestors(ASTMethodDeclaration.class) + .firstOpt() + .map(ASTMethodDeclaration::getName) + .map(Stream::of) + .orElse(Stream.empty()) + ) + ) + .collect(Collectors.toSet()); Map> consideredNames = file.descendants(ASTMethodDeclaration.class) From 597f3f37dc869cd797eb9b7e37ff949a98143148 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Tue, 23 Jul 2024 20:09:50 +0200 Subject: [PATCH 10/58] [doc] Update release notes (#5110, #5111) --- docs/pages/release_notes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 8038d97525..235d9616f2 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -15,6 +15,8 @@ This is a {{ site.pmd.release_type }} release. ### ๐Ÿš€ New and noteworthy ### ๐Ÿ› Fixed Issues +* java-bestpractices + * [#5110](https://github.com/pmd/pmd/issues/5110): \[java] UnusedPrivateMethod for method referenced by lombok.Builder.ObtainVia * plsql * [#5086](https://github.com/pmd/pmd/pull/5086): \[plsql] Fixed issue with missing optional table alias in MERGE usage * [#5087](https://github.com/pmd/pmd/pull/5087): \[plsql] Add support for SQL_MACRO @@ -27,6 +29,7 @@ This is a {{ site.pmd.release_type }} release. * [#5087](https://github.com/pmd/pmd/pull/5087): \[plsql] Add support for SQL_MACRO - [Arjen Duursma](https://github.com/duursma) (@duursma) * [#5088](https://github.com/pmd/pmd/pull/5088): \[plsql] Add support for 'DEFAULT' clause on the arguments of some oracle functions - [Arjen Duursma](https://github.com/duursma) (@duursma) * [#5107](https://github.com/pmd/pmd/pull/5107): \[doc] Update maven.md - Typo fixed for maven target - [karthikaiyasamy](https://github.com/karthikaiyasamy) (@karthikaiyasamy) +* [#5111](https://github.com/pmd/pmd/pull/5111): \[java] Fix UnusedPrivateMethod for @lombok.Builder.ObtainVia - [Krzysztof Debski](https://github.com/kdebski85) (@kdebski85) {% endtocmaker %} From a3f9d4af5934f1050c94b887db0fe579326e218e Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 25 Jul 2024 09:57:20 +0200 Subject: [PATCH 11/58] [java] Simplify UnusedPrivateMethodRule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Clรฉment Fournier --- .../java/rule/bestpractices/UnusedPrivateMethodRule.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java index b346f1a80c..f26108d9dc 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/UnusedPrivateMethodRule.java @@ -74,10 +74,9 @@ public class UnusedPrivateMethodRule extends AbstractIgnoredAnnotationRule { .filter(it -> TypeTestUtil.isA("org.junit.jupiter.params.provider.MethodSource", it) && it.getFlatValue("value").isEmpty()) .ancestors(ASTMethodDeclaration.class) - .firstOpt() + .take(1) + .toStream() .map(ASTMethodDeclaration::getName) - .map(Stream::of) - .orElse(Stream.empty()) ) ) .collect(Collectors.toSet()); From 87b0c4f851179828721f8f391778fe4d9107ba74 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 25 Jul 2024 11:17:49 +0200 Subject: [PATCH 12/58] [plsql] Fix ScalarDataTypeName parsing Fixes #5133 --- docs/pages/release_notes.md | 1 + pmd-plsql/etc/grammar/PLSQL.jjt | 8 ++------ .../pmd/lang/plsql/ast/PlsqlTreeDumpTest.java | 5 +++++ .../lang/plsql/ast/Issue5133SubTypeDefinition.pls | 8 ++++++++ .../lang/plsql/ast/Issue5133SubTypeDefinition.txt | 13 +++++++++++++ 5 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/Issue5133SubTypeDefinition.pls create mode 100644 pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/Issue5133SubTypeDefinition.txt diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 2826a1432b..24f0987147 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -50,6 +50,7 @@ This is a {{ site.pmd.release_type }} release. * [#5086](https://github.com/pmd/pmd/pull/5086): \[plsql] Fixed issue with missing optional table alias in MERGE usage * [#5087](https://github.com/pmd/pmd/pull/5087): \[plsql] Add support for SQL_MACRO * [#5088](https://github.com/pmd/pmd/pull/5088): \[plsql] Add support for 'DEFAULT' clause on the arguments of some oracle functions + * [#5133](https://github.com/pmd/pmd/issues/5133): \[plsql] AssertionError: Root of the tree should implement RootNode for a PL/SQL type declaration * cli * [#5120](https://github.com/pmd/pmd/issues/5120): \[cli] Can't start designer under Windows * core diff --git a/pmd-plsql/etc/grammar/PLSQL.jjt b/pmd-plsql/etc/grammar/PLSQL.jjt index c5d970e242..787470b79e 100644 --- a/pmd-plsql/etc/grammar/PLSQL.jjt +++ b/pmd-plsql/etc/grammar/PLSQL.jjt @@ -993,12 +993,8 @@ ASTScalarDataTypeName ScalarDataTypeName() : LOOKAHEAD(4) ( + + + #5132 [plsql] TomKytesDespair - exception for more complex exception handler + 0 + + From f1f376d24803a3ebffdc64721c75999fceb48067 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 25 Jul 2024 12:23:04 +0200 Subject: [PATCH 22/58] Deprecate generated parser implementations --- docs/pages/release_notes.md | 9 +++++++++ pmd-jsp/etc/grammar/Jsp.jjt | 3 +++ pmd-plsql/etc/grammar/PLSQL.jjt | 2 +- pmd-velocity/etc/grammar/Vtl.jjt | 5 ++++- pmd-visualforce/etc/grammar/Vf.jjt | 5 +++++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index bc1a36ec7a..bbef7173be 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -17,6 +17,15 @@ This is a {{ site.pmd.release_type }} release. ### ๐Ÿ› Fixed Issues ### ๐Ÿšจ API Changes +* pmd-jsp + * {%jdoc jsp::lang.jsp.ast.JspParserImpl %} is deprecated now. It should have been package-private + because this is an implementation class that should not be used directly. +* pmd-velocity + * {%jdoc velocity::lang.velocity.ast.VtlParserImpl %} is deprecated now. It should have been package-private + because this is an implementation class that should not be used directly. +* pmd-visualforce + * {%jdoc visualforce::lang.visualforce.ast.VfParserImpl %} is deprecated now. It should have been package-private + because this is an implementation class that should not be used directly. ### โœจ External Contributions diff --git a/pmd-jsp/etc/grammar/Jsp.jjt b/pmd-jsp/etc/grammar/Jsp.jjt index 1f74b3dcf7..c87cb23aae 100644 --- a/pmd-jsp/etc/grammar/Jsp.jjt +++ b/pmd-jsp/etc/grammar/Jsp.jjt @@ -33,7 +33,10 @@ package net.sourceforge.pmd.lang.jsp.ast; /** * JSP Parser for PMD. * @author Pieter, Application Engineers NV/SA, http://www.ae.be + * @deprecated Since 7.5.0. JspParserImpl should have been package private because this is an implementation class + * that should not be used directly. */ +@Deprecated public class JspParserImpl { diff --git a/pmd-plsql/etc/grammar/PLSQL.jjt b/pmd-plsql/etc/grammar/PLSQL.jjt index c9f2f4b0d9..81224d0402 100644 --- a/pmd-plsql/etc/grammar/PLSQL.jjt +++ b/pmd-plsql/etc/grammar/PLSQL.jjt @@ -176,7 +176,7 @@ import java.util.ArrayList; import java.util.List; /** - * @deprecated PLSQLParserImpl should have been package private because this is an implementation class + * @deprecated Since 7.3.0. PLSQLParserImpl should have been package private because this is an implementation class * that should not be used directly. */ @Deprecated diff --git a/pmd-velocity/etc/grammar/Vtl.jjt b/pmd-velocity/etc/grammar/Vtl.jjt index 74804d4dd0..3c9cbb533a 100644 --- a/pmd-velocity/etc/grammar/Vtl.jjt +++ b/pmd-velocity/etc/grammar/Vtl.jjt @@ -58,7 +58,10 @@ import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken; * @author Geir Magnusson Jr. * @author Henning P. Schmiedehausen * @version $Id$ -*/ + * @deprecated Since 7.5.0. VtlParserImpl should have been package private because this is an implementation class + * that should not be used directly. + */ +@Deprecated public class VtlParserImpl { private void throwParseException(String message) { diff --git a/pmd-visualforce/etc/grammar/Vf.jjt b/pmd-visualforce/etc/grammar/Vf.jjt index 6fa854e7c3..81c31b2df7 100644 --- a/pmd-visualforce/etc/grammar/Vf.jjt +++ b/pmd-visualforce/etc/grammar/Vf.jjt @@ -13,6 +13,11 @@ options { PARSER_BEGIN(VfParserImpl) package net.sourceforge.pmd.lang.visualforce.ast; +/** + * @deprecated Since 7.5.0. VfParserImpl should have been package private because this is an implementation class + * that should not be used directly. + */ +@Deprecated public class VfParserImpl { From dacc8b114b4805fae777abf4d2a0d9e9043cde2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 17:36:58 +0200 Subject: [PATCH 23/58] Bump org.apache.maven.plugins:maven-checkstyle-plugin from 3.3.1 to 3.4.0 (#5141) Bumps [org.apache.maven.plugins:maven-checkstyle-plugin](https://github.com/apache/maven-checkstyle-plugin) from 3.3.1 to 3.4.0. - [Commits](https://github.com/apache/maven-checkstyle-plugin/compare/maven-checkstyle-plugin-3.3.1...maven-checkstyle-plugin-3.4.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-checkstyle-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e15927ad7d..409fe0440c 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,7 @@ 5.0 3.2.5 10.14.0 - 3.3.1 + 3.4.0 3.24.0 1.10.14 3.6.3 From b7d9eaaa97915bb0d327540582172a9db77112f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 17:46:08 +0200 Subject: [PATCH 24/58] Bump org.apache.maven.plugins:maven-compiler-plugin from 3.12.1 to 3.13.0 (#5142) Bumps [org.apache.maven.plugins:maven-compiler-plugin](https://github.com/apache/maven-compiler-plugin) from 3.12.1 to 3.13.0. - [Release notes](https://github.com/apache/maven-compiler-plugin/releases) - [Commits](https://github.com/apache/maven-compiler-plugin/compare/maven-compiler-plugin-3.12.1...maven-compiler-plugin-3.13.0) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-compiler-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 409fe0440c..4278bc8d11 100644 --- a/pom.xml +++ b/pom.xml @@ -265,7 +265,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.12.1 + 3.13.0 ${java.version} From d2fbe14afaf2e040e5efbd7a861f518e16a94e74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Jul 2024 18:06:14 +0200 Subject: [PATCH 25/58] Bump org.codehaus.mojo:versions-maven-plugin from 2.16.2 to 2.17.1 (#5144) Bumps [org.codehaus.mojo:versions-maven-plugin](https://github.com/mojohaus/versions) from 2.16.2 to 2.17.1. - [Release notes](https://github.com/mojohaus/versions/releases) - [Changelog](https://github.com/mojohaus/versions/blob/master/ReleaseNotes.md) - [Commits](https://github.com/mojohaus/versions/compare/2.16.2...2.17.1) --- updated-dependencies: - dependency-name: org.codehaus.mojo:versions-maven-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 4278bc8d11..2668b05875 100644 --- a/pom.xml +++ b/pom.xml @@ -595,7 +595,7 @@ org.codehaus.mojo versions-maven-plugin - 2.16.2 + 2.17.1 org.sonatype.plugins From 3735fd145b1385c914bcea1ac363866e33cc7ede Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Sat, 27 Jul 2024 20:15:23 +0200 Subject: [PATCH 26/58] [apex] Fix parsing of triggers with declarations Only the grandchildren of a trigger block were ending up in the tree, but the direct children of triggerBlock were missing, e.g. ForLoopStatement. This caused OperationWithHighCostInLoop to not find the loop anymore in triggers. This will probably fix other false negatives in triggers in other rules as well. Fixes #5139 --- docs/pages/release_notes.md | 2 + .../pmd/lang/apex/ast/ASTMethod.java | 17 +++++++ .../pmd/lang/apex/ast/ApexTreeBuilder.kt | 16 ++++-- .../pmd/lang/apex/ast/ApexTreeDumpTest.java | 5 ++ .../pmd/lang/apex/ast/AccountTrigger.cls | 9 ++++ .../pmd/lang/apex/ast/AccountTrigger.txt | 50 +++++++++++++++++++ .../rule/design/xml/CyclomaticComplexity.xml | 2 +- .../xml/OperationWithHighCostInLoop.xml | 14 ++++++ 8 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/AccountTrigger.cls create mode 100644 pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/AccountTrigger.txt diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 7097969edc..39aef42caa 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -15,6 +15,8 @@ This is a {{ site.pmd.release_type }} release. ### ๐Ÿš€ New and noteworthy ### ๐Ÿ› Fixed Issues +* apex-performance + * [#5139](https://github.com/pmd/pmd/issues/5139): \[apex] OperationWithHighCostInLoop not firing in triggers * plsql-bestpractices * [#5132](https://github.com/pmd/pmd/issues/5132): \[plsql] TomKytesDespair - exception for more complex exception handler diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTMethod.java b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTMethod.java index 69367768be..3f44ef5430 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTMethod.java +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ASTMethod.java @@ -10,6 +10,7 @@ import java.util.stream.Collectors; import net.sourceforge.pmd.lang.document.TextDocument; import net.sourceforge.pmd.lang.document.TextPos2d; import net.sourceforge.pmd.lang.document.TextRegion; +import net.sourceforge.pmd.lang.rule.xpath.NoAttribute; import com.google.summit.ast.SourceLocation; import com.google.summit.ast.declaration.MethodDeclaration; @@ -27,6 +28,12 @@ public final class ASTMethod extends AbstractApexNode implements ApexQualifiable */ private static final String STATIC_INIT_ID = ""; + /** + * Internal name used by the synthetic trigger method. + * @see #isTriggerBlock() + */ + private static final String TRIGGER_INVOKE_ID = ""; + // Store the details instead of wrapping a com.google.summit.ast.Node. // This is to allow synthetic ASTMethod nodes. // An example is the trigger `invoke` method. @@ -150,4 +157,14 @@ public final class ASTMethod extends AbstractApexNode implements ApexQualifiable public int getArity() { return parameterTypes.size(); } + + /** + * Checks whether this method is the synthetic trigger method. + * @return true if this method is the synthetic trigger method + * @since 7.5.0 + */ + @NoAttribute + public boolean isTriggerBlock() { + return TRIGGER_INVOKE_ID.equals(internalName); + } } diff --git a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.kt b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.kt index a41745ce93..20dd5621aa 100644 --- a/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.kt +++ b/pmd-apex/src/main/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeBuilder.kt @@ -229,7 +229,7 @@ class ApexTreeBuilder(private val task: ParserTask, private val proc: ApexLangua // 2. Add the expected ASTModifier child node buildModifiers(emptyList()).also { it.setParent(invokeMethod) } // 3. Elide the body CompoundStatement->ASTBlockStatement - node.body.forEach { buildChildren(it, parent = invokeMethod as AbstractApexNode) } + node.body.forEach { buildAndSetParent(it, parent = invokeMethod as AbstractApexNode) } } else { buildChildren(node, parent = this, exclude = { it in node.modifiers }) } @@ -737,18 +737,18 @@ class ApexTreeBuilder(private val task: ParserTask, private val proc: ApexLangua findDescendants(root, nodeType = ASTProperty::class).forEach { node -> generateFields(node) } // Sort resulting nodes - findDescendants(root, nodeType = ASTUserClass::class).forEach { node -> + findDescendants(root, nodeType = BaseApexClass::class).forEach { node -> sortUserClassChildren(node) } } /** - * Sort children of [ASTUserClass] in historical order. + * Sort children of [BaseApexClass] (ASTUserClass, ASTUserTrigger, ...) in historical order. * * This sorts [ASTField] nodes immediately after [ASTModifierNode] nodes at * the start of the ordered children. */ - private fun sortUserClassChildren(node: ASTUserClass) { + private fun sortUserClassChildren(node: BaseApexClass<*>) { val children = ArrayList(node.children().toList()) children.sortBy{ when (it) { @@ -772,7 +772,13 @@ class ApexTreeBuilder(private val task: ParserTask, private val proc: ApexLangua /** Generates [ASTField] nodes for the [ASTFieldDeclarationStatements]. */ private fun generateFields(node: ASTFieldDeclarationStatements) { - val parent = node.parent as BaseApexClass<*> + val parent = if (node.parent is BaseApexClass<*>) { + node.parent as BaseApexClass<*> + } else if (node.parent is ASTMethod && (node.parent as ASTMethod).isTriggerBlock) { + node.parent.parent as BaseApexClass<*> + } else { + throw IllegalStateException("Unexpected apex tree - field declaration $node cannot appear hear") + } node.node.declarations .map { decl -> diff --git a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeDumpTest.java b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeDumpTest.java index 0bba0ee4f8..997d76819e 100644 --- a/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeDumpTest.java +++ b/pmd-apex/src/test/java/net/sourceforge/pmd/lang/apex/ast/ApexTreeDumpTest.java @@ -65,4 +65,9 @@ class ApexTreeDumpTest extends BaseTreeDumpTest { void switchStatements() { doTest("SwitchStatements"); } + + @Test + void trigger() { + doTest("AccountTrigger"); + } } diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/AccountTrigger.cls b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/AccountTrigger.cls new file mode 100644 index 0000000000..39b81e2263 --- /dev/null +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/AccountTrigger.cls @@ -0,0 +1,9 @@ +// see https://github.com/pmd/pmd/issues/5139 +trigger AccountTrigger on Account (before insert, before update) { + integer i = 0; + for (i = 0; i <15; i++) { + SObjectType token = Schema.getGlobalDescribe().get('Account'); + } + integer anotherField = 2; + System.debug('test'); +} diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/AccountTrigger.txt b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/AccountTrigger.txt new file mode 100644 index 0000000000..b5999de53f --- /dev/null +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/ast/AccountTrigger.txt @@ -0,0 +1,50 @@ ++- ApexFile[@DefiningType = "AccountTrigger", @RealLoc = true] + +- UserTrigger[@DefiningType = "AccountTrigger", @Image = "AccountTrigger", @Nested = false, @RealLoc = true, @SimpleName = "AccountTrigger", @TargetName = "Account", @Usages = (TriggerUsage.BEFORE_INSERT, TriggerUsage.BEFORE_UPDATE)] + +- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false] + +- Field[@DefiningType = "AccountTrigger", @Image = "i", @Name = "i", @RealLoc = true, @Type = "Integer", @Value = "0"] + | +- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false] + +- Field[@DefiningType = "AccountTrigger", @Image = "anotherField", @Name = "anotherField", @RealLoc = true, @Type = "Integer", @Value = "2"] + | +- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false] + +- Method[@Arity = 0, @CanonicalName = "invoke", @Constructor = false, @DefiningType = "AccountTrigger", @Image = "invoke", @RealLoc = false, @ReturnType = "void", @StaticInitializer = false] + +- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false] + +- FieldDeclarationStatements[@DefiningType = "AccountTrigger", @RealLoc = true, @TypeArguments = (), @TypeName = "Integer"] + | +- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false] + | +- FieldDeclaration[@DefiningType = "AccountTrigger", @Image = "i", @Name = "i", @RealLoc = true] + | +- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "AccountTrigger", @Double = false, @Image = "0", @Integer = true, @LiteralType = LiteralType.INTEGER, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = false] + | +- VariableExpression[@DefiningType = "AccountTrigger", @Image = "i", @RealLoc = true] + | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false] + +- ForLoopStatement[@DefiningType = "AccountTrigger", @RealLoc = true] + | +- StandardCondition[@DefiningType = "AccountTrigger", @RealLoc = true] + | | +- BooleanExpression[@DefiningType = "AccountTrigger", @Op = BooleanOperator.LESS_THAN, @RealLoc = true] + | | +- VariableExpression[@DefiningType = "AccountTrigger", @Image = "i", @RealLoc = true] + | | | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false] + | | +- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "AccountTrigger", @Double = false, @Image = "15", @Integer = true, @LiteralType = LiteralType.INTEGER, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = false] + | +- Expression[@DefiningType = "AccountTrigger", @RealLoc = true] + | | +- AssignmentExpression[@DefiningType = "AccountTrigger", @Op = AssignmentOperator.EQUALS, @RealLoc = true] + | | +- VariableExpression[@DefiningType = "AccountTrigger", @Image = "i", @RealLoc = true] + | | | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false] + | | +- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "AccountTrigger", @Double = false, @Image = "0", @Integer = true, @LiteralType = LiteralType.INTEGER, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = false] + | +- BlockStatement[@CurlyBrace = true, @DefiningType = "AccountTrigger", @RealLoc = true] + | | +- VariableDeclarationStatements[@DefiningType = "AccountTrigger", @RealLoc = true] + | | +- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false] + | | +- VariableDeclaration[@DefiningType = "AccountTrigger", @Image = "token", @RealLoc = true, @Type = "SObjectType"] + | | +- MethodCallExpression[@DefiningType = "AccountTrigger", @FullMethodName = "get", @InputParametersSize = 1, @MethodName = "get", @RealLoc = true] + | | | +- ReferenceExpression[@DefiningType = "AccountTrigger", @Image = "", @RealLoc = false, @ReferenceType = ReferenceType.METHOD, @SObjectType = false, @SafeNav = false] + | | | | +- MethodCallExpression[@DefiningType = "AccountTrigger", @FullMethodName = "Schema.getGlobalDescribe", @InputParametersSize = 0, @MethodName = "getGlobalDescribe", @RealLoc = true] + | | | | +- ReferenceExpression[@DefiningType = "AccountTrigger", @Image = "Schema", @RealLoc = true, @ReferenceType = ReferenceType.METHOD, @SObjectType = false, @SafeNav = false] + | | | +- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "AccountTrigger", @Double = false, @Image = "Account", @Integer = false, @LiteralType = LiteralType.STRING, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = true] + | | +- VariableExpression[@DefiningType = "AccountTrigger", @Image = "token", @RealLoc = true] + | | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false] + | +- PostfixExpression[@DefiningType = "AccountTrigger", @Op = PostfixOperator.INCREMENT, @RealLoc = true] + | +- VariableExpression[@DefiningType = "AccountTrigger", @Image = "i", @RealLoc = true] + | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false] + +- FieldDeclarationStatements[@DefiningType = "AccountTrigger", @RealLoc = true, @TypeArguments = (), @TypeName = "Integer"] + | +- ModifierNode[@Abstract = false, @DefiningType = "AccountTrigger", @DeprecatedTestMethod = false, @Final = false, @Global = false, @InheritedSharing = false, @Modifiers = 0, @Override = false, @Private = false, @Protected = false, @Public = false, @RealLoc = false, @Static = false, @Test = false, @TestOrTestSetup = false, @Transient = false, @Virtual = false, @WebService = false, @WithSharing = false, @WithoutSharing = false] + | +- FieldDeclaration[@DefiningType = "AccountTrigger", @Image = "anotherField", @Name = "anotherField", @RealLoc = true] + | +- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "AccountTrigger", @Double = false, @Image = "2", @Integer = true, @LiteralType = LiteralType.INTEGER, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = false] + | +- VariableExpression[@DefiningType = "AccountTrigger", @Image = "anotherField", @RealLoc = true] + | +- EmptyReferenceExpression[@DefiningType = null, @RealLoc = false] + +- ExpressionStatement[@DefiningType = "AccountTrigger", @RealLoc = true] + +- MethodCallExpression[@DefiningType = "AccountTrigger", @FullMethodName = "System.debug", @InputParametersSize = 1, @MethodName = "debug", @RealLoc = true] + +- ReferenceExpression[@DefiningType = "AccountTrigger", @Image = "System", @RealLoc = true, @ReferenceType = ReferenceType.METHOD, @SObjectType = false, @SafeNav = false] + +- LiteralExpression[@Boolean = false, @Decimal = false, @DefiningType = "AccountTrigger", @Double = false, @Image = "test", @Integer = false, @LiteralType = LiteralType.STRING, @Long = false, @Name = null, @Null = false, @RealLoc = true, @String = true] diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/CyclomaticComplexity.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/CyclomaticComplexity.xml index fd282bac4a..8fcbd0b6d7 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/CyclomaticComplexity.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/design/xml/CyclomaticComplexity.xml @@ -263,7 +263,7 @@ trigger CaseAssignLevel on CaseAssignLevel__c (after delete, after insert, after 1 1 - The trigger 'CaseAssignLevel' has a cyclomatic complexity of 9. + The trigger 'CaseAssignLevel' has a cyclomatic complexity of 12. diff --git a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/performance/xml/OperationWithHighCostInLoop.xml b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/performance/xml/OperationWithHighCostInLoop.xml index 15a05db1a0..e03e0ea17b 100644 --- a/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/performance/xml/OperationWithHighCostInLoop.xml +++ b/pmd-apex/src/test/resources/net/sourceforge/pmd/lang/apex/rule/performance/xml/OperationWithHighCostInLoop.xml @@ -135,4 +135,18 @@ public class Foo { ]]> + + + #5139 [apex] OperationWithHighCostInLoop not firing in triggers + 1 + 4 + + From 65b6d5e796b74be275f772eb167fb2a2e24dd247 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Jul 2024 03:54:56 +0000 Subject: [PATCH 27/58] Bump org.apache.commons:commons-text from 1.11.0 to 1.12.0 Bumps org.apache.commons:commons-text from 1.11.0 to 1.12.0. --- updated-dependencies: - dependency-name: org.apache.commons:commons-text dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2668b05875..a60e767f31 100644 --- a/pom.xml +++ b/pom.xml @@ -855,7 +855,7 @@ org.apache.commons commons-text - 1.11.0 + 1.12.0 org.slf4j From 49cfe186ae938422b58faca2fa8ac25f17644fec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 03:18:42 +0000 Subject: [PATCH 28/58] Bump org.pcollections:pcollections from 3.2.0 to 4.0.2 Bumps [org.pcollections:pcollections](https://github.com/hrldcpr/pcollections) from 3.2.0 to 4.0.2. - [Changelog](https://github.com/hrldcpr/pcollections/blob/master/CHANGELOG.md) - [Commits](https://github.com/hrldcpr/pcollections/compare/v3.2.0...v4.0.2) --- updated-dependencies: - dependency-name: org.pcollections:pcollections dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index a60e767f31..623f083eca 100644 --- a/pom.xml +++ b/pom.xml @@ -820,7 +820,7 @@ org.pcollections pcollections - 3.2.0 + 4.0.2 net.sourceforge.pmd From c5de5d67a0d426252565f9785b69844363821e42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 03:57:58 +0000 Subject: [PATCH 29/58] Bump danger from 9.4.3 to 9.5.0 in the all-gems group across 1 directory Bumps the all-gems group with 1 update in the / directory: [danger](https://github.com/danger/danger). Updates `danger` from 9.4.3 to 9.5.0 - [Release notes](https://github.com/danger/danger/releases) - [Changelog](https://github.com/danger/danger/blob/master/CHANGELOG.md) - [Commits](https://github.com/danger/danger/commits) --- updated-dependencies: - dependency-name: danger dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-gems ... Signed-off-by: dependabot[bot] --- Gemfile.lock | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index f8b1e9cb86..3fdfbb4ca7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -13,7 +13,7 @@ GEM concurrent-ruby (1.3.3) cork (0.3.0) colored2 (~> 3.1) - danger (9.4.3) + danger (9.5.0) claide (~> 1.0) claide-plugins (>= 0.9.2) colored2 (~> 3.1) @@ -23,18 +23,17 @@ GEM git (~> 1.13) kramdown (~> 2.3) kramdown-parser-gfm (~> 1.0) - no_proxy_fix octokit (>= 4.0) terminal-table (>= 1, < 4) differ (0.1.2) et-orbi (1.2.11) tzinfo - faraday (2.10.0) + faraday (2.10.1) faraday-net_http (>= 2.0, < 3.2) logger faraday-http-cache (2.5.1) faraday (>= 0.8) - faraday-net_http (3.1.0) + faraday-net_http (3.1.1) net-http fugit (1.11.0) et-orbi (~> 1, >= 1.2.11) @@ -52,7 +51,6 @@ GEM nap (1.1.0) net-http (0.4.1) uri - no_proxy_fix (0.1.2) nokogiri (1.16.6-x86_64-linux) racc (~> 1.4) octokit (9.1.0) @@ -70,7 +68,7 @@ GEM raabro (1.4.0) racc (1.8.0) rchardet (1.8.0) - rexml (3.3.2) + rexml (3.3.4) strscan rouge (4.3.0) rufus-scheduler (3.9.1) From cc5fc196c51978413996ae99ee152f125fe92be6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Aug 2024 03:37:24 +0000 Subject: [PATCH 30/58] Bump org.apache.maven.plugins:maven-dependency-plugin Bumps [org.apache.maven.plugins:maven-dependency-plugin](https://github.com/apache/maven-dependency-plugin) from 3.6.1 to 3.7.1. - [Release notes](https://github.com/apache/maven-dependency-plugin/releases) - [Commits](https://github.com/apache/maven-dependency-plugin/compare/maven-dependency-plugin-3.6.1...maven-dependency-plugin-3.7.1) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-dependency-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 623f083eca..c525a66db0 100644 --- a/pom.xml +++ b/pom.xml @@ -177,7 +177,7 @@ org.apache.maven.plugins maven-dependency-plugin - 3.6.1 + 3.7.1 org.apache.maven.plugins From 623de394ece515d109391acb5430afe65315d5f5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:53:15 +0200 Subject: [PATCH 31/58] Bump org.apache.maven.plugins:maven-site-plugin from 4.0.0-M13 to 4.0.0-M16 (#5149) Bumps [org.apache.maven.plugins:maven-site-plugin](https://github.com/apache/maven-site-plugin) from 4.0.0-M13 to 4.0.0-M16. - [Commits](https://github.com/apache/maven-site-plugin/compare/maven-site-plugin-4.0.0-M13...maven-site-plugin-4.0.0-M16) --- updated-dependencies: - dependency-name: org.apache.maven.plugins:maven-site-plugin dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c525a66db0..39b335a742 100644 --- a/pom.xml +++ b/pom.xml @@ -590,7 +590,7 @@ org.apache.maven.plugins maven-site-plugin - 4.0.0-M13 + 4.0.0-M16 org.codehaus.mojo From 2192256b5a5cf3a88733a8305c2808b93519f649 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Aug 2024 12:54:41 +0200 Subject: [PATCH 32/58] Bump jekyll and github-pages (#5165) Bumps the all-gems group with 1 update in the /docs directory: [jekyll](https://github.com/jekyll/jekyll). Updates `jekyll` from 3.9.5 to 3.10.0 - [Release notes](https://github.com/jekyll/jekyll/releases) - [Changelog](https://github.com/jekyll/jekyll/blob/master/History.markdown) - [Commits](https://github.com/jekyll/jekyll/compare/v3.9.5...v3.10.0) Updates `github-pages` from 231 to 232 - [Release notes](https://github.com/github/pages-gem/releases) - [Commits](https://github.com/github/pages-gem/compare/v231...v232) --- updated-dependencies: - dependency-name: jekyll dependency-type: direct:production update-type: version-update:semver-minor dependency-group: all-gems - dependency-name: github-pages dependency-type: direct:production update-type: version-update:semver-major dependency-group: all-gems ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/Gemfile.lock | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index ffb75f2b77..5901351a72 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -21,7 +21,7 @@ GEM coffee-script-source (1.12.2) colorator (1.1.0) commonmarker (0.23.10) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) connection_pool (2.4.1) csv (3.3.0) dnsruby (1.72.2) @@ -42,12 +42,12 @@ GEM ffi (1.17.0-x86_64-linux-gnu) forwardable-extended (2.6.0) gemoji (4.1.0) - github-pages (231) + github-pages (232) github-pages-health-check (= 1.18.2) - jekyll (= 3.9.5) + jekyll (= 3.10.0) jekyll-avatar (= 0.8.0) jekyll-coffeescript (= 1.2.2) - jekyll-commonmark-ghpages (= 0.4.0) + jekyll-commonmark-ghpages (= 0.5.1) jekyll-default-layout (= 0.1.5) jekyll-feed (= 0.17.0) jekyll-gist (= 1.5.0) @@ -84,9 +84,10 @@ GEM liquid (= 4.0.4) mercenary (~> 0.3) minima (= 2.5.1) - nokogiri (>= 1.13.6, < 2.0) + nokogiri (>= 1.16.2, < 2.0) rouge (= 3.30.0) terminal-table (~> 1.4) + webrick (~> 1.8) github-pages-health-check (1.18.2) addressable (~> 2.3) dnsruby (~> 1.60) @@ -99,9 +100,10 @@ GEM http_parser.rb (0.8.0) i18n (1.14.5) concurrent-ruby (~> 1.0) - jekyll (3.9.5) + jekyll (3.10.0) addressable (~> 2.4) colorator (~> 1.0) + csv (~> 3.0) em-websocket (~> 0.5) i18n (>= 0.7, < 2) jekyll-sass-converter (~> 1.0) @@ -112,6 +114,7 @@ GEM pathutil (~> 0.9) rouge (>= 1.7, < 4) safe_yaml (~> 1.0) + webrick (>= 1.0) jekyll-avatar (0.8.0) jekyll (>= 3.0, < 5.0) jekyll-coffeescript (1.2.2) @@ -119,9 +122,9 @@ GEM coffee-script-source (~> 1.12) jekyll-commonmark (1.4.0) commonmarker (~> 0.22) - jekyll-commonmark-ghpages (0.4.0) - commonmarker (~> 0.23.7) - jekyll (~> 3.9.0) + jekyll-commonmark-ghpages (0.5.1) + commonmarker (>= 0.23.7, < 1.1.0) + jekyll (>= 3.9, < 4.0) jekyll-commonmark (~> 1.4.0) rouge (>= 2.0, < 5.0) jekyll-default-layout (0.1.5) @@ -237,7 +240,7 @@ GEM rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rexml (3.3.2) + rexml (3.3.5) strscan rouge (3.30.0) rubyzip (2.3.2) From ca34c3edd3cde91a949397632031b71212034b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sun, 18 Aug 2024 19:22:09 -0300 Subject: [PATCH 33/58] Add minimal failing case for #5151 --- .../bestpractices/xml/GuardLogStatement.xml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml index d1e8ad23ae..d76238c8a0 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml @@ -578,6 +578,27 @@ public class GuardLogStatementTest { final Logger logger = LogManager.getLogger(GuardLogStatementTest.class); logger.info("Some info: {}", GuardLogStatementTest::foo); } +} + ]]> + + + + #5151 Field accesses do not require guards + 0 + From 792d91e062413f00f5e28db9629748fb654c9d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sun, 18 Aug 2024 19:23:25 -0300 Subject: [PATCH 34/58] Fix GuardLogStatementRule for field accesses --- .../lang/java/rule/bestpractices/GuardLogStatementRule.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java index 1301e2a403..334cb62a11 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java @@ -18,6 +18,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; +import net.sourceforge.pmd.lang.java.ast.ASTFieldAccess; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression; @@ -237,7 +238,9 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { // so that we can ignore it return call.getArguments().toStream() .drop(messageArgIndex) // remove the level argument if needed - .all(it -> it instanceof ASTStringLiteral || it instanceof ASTLambdaExpression || it instanceof ASTVariableAccess || it instanceof ASTMethodReference); + .all(it -> it instanceof ASTStringLiteral || it instanceof ASTLambdaExpression + || it instanceof ASTVariableAccess || it instanceof ASTMethodReference + || it instanceof ASTFieldAccess); } private void extractProperties() { From 0837be674e3380bb9489ddc32942b0fcf22ac29b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sun, 18 Aug 2024 19:24:46 -0300 Subject: [PATCH 35/58] Update changelog, refs #5151 --- docs/pages/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 39aef42caa..3251558d4a 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -17,6 +17,8 @@ This is a {{ site.pmd.release_type }} release. ### ๐Ÿ› Fixed Issues * apex-performance * [#5139](https://github.com/pmd/pmd/issues/5139): \[apex] OperationWithHighCostInLoop not firing in triggers +* java-bestpractices + * [#5151](https://github.com/pmd/pmd/issues/5151): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is a constant from another class * plsql-bestpractices * [#5132](https://github.com/pmd/pmd/issues/5132): \[plsql] TomKytesDespair - exception for more complex exception handler From 176fae39d29b967980a40c61d1ceabe0afcb813e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sun, 18 Aug 2024 19:29:39 -0300 Subject: [PATCH 36/58] Add test for #5152 --- .../bestpractices/xml/GuardLogStatement.xml | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml index d76238c8a0..23909014e3 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml @@ -595,8 +595,28 @@ public class GuardLogStatementTest { public void test() { LOG.info( - "Some message here : foo={}", - GuardLogStatementTest.MY_CONSTANT + "Some message here : foo={}", + GuardLogStatementTest.MY_CONSTANT + ); + } +} + ]]> + + + + #5152 "this" do not require guards + 0 + Date: Sun, 18 Aug 2024 19:29:48 -0300 Subject: [PATCH 37/58] Fix #5152 --- .../lang/java/rule/bestpractices/GuardLogStatementRule.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java index 334cb62a11..98c950720e 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java @@ -25,6 +25,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodReference; import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; +import net.sourceforge.pmd.lang.java.ast.ASTThisExpression; import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; import net.sourceforge.pmd.lang.java.ast.ASTVariableId; import net.sourceforge.pmd.lang.java.ast.BinaryOp; @@ -240,7 +241,7 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { .drop(messageArgIndex) // remove the level argument if needed .all(it -> it instanceof ASTStringLiteral || it instanceof ASTLambdaExpression || it instanceof ASTVariableAccess || it instanceof ASTMethodReference - || it instanceof ASTFieldAccess); + || it instanceof ASTFieldAccess || it instanceof ASTThisExpression); } private void extractProperties() { From 33e7e71bfaa083ccce3cc633b06b4b99afe61de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sun, 18 Aug 2024 19:30:34 -0300 Subject: [PATCH 38/58] Update changelog, refs #5152 --- docs/pages/release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 3251558d4a..90b38429cc 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -19,6 +19,7 @@ This is a {{ site.pmd.release_type }} release. * [#5139](https://github.com/pmd/pmd/issues/5139): \[apex] OperationWithHighCostInLoop not firing in triggers * java-bestpractices * [#5151](https://github.com/pmd/pmd/issues/5151): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is a constant from another class + * [#5152](https://github.com/pmd/pmd/issues/5152): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is "this" * plsql-bestpractices * [#5132](https://github.com/pmd/pmd/issues/5132): \[plsql] TomKytesDespair - exception for more complex exception handler From 4352b53aece5d18c07c8778f8f1e1dc17a28cc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sun, 18 Aug 2024 19:40:51 -0300 Subject: [PATCH 39/58] Add tests regarding array accesses --- .../bestpractices/xml/GuardLogStatement.xml | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml index 23909014e3..f2f91314b8 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml @@ -619,6 +619,51 @@ public class GuardLogStatementTest { this ); } +} + ]]> + + + + #5153 array access to constants do not require guards + 0 + + + + + #5153 array access to method returned values require guards + 1 + From 9ef927067e91bc35b08117ea403a8151d461e5fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sun, 18 Aug 2024 19:41:06 -0300 Subject: [PATCH 40/58] Fix tests regarding array accesses --- .../rule/bestpractices/GuardLogStatementRule.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java index 98c950720e..43ff247174 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java @@ -15,6 +15,7 @@ import java.util.Map; import org.checkerframework.checker.nullness.qual.Nullable; import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; +import net.sourceforge.pmd.lang.java.ast.ASTArrayAccess; import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; @@ -239,9 +240,14 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { // so that we can ignore it return call.getArguments().toStream() .drop(messageArgIndex) // remove the level argument if needed - .all(it -> it instanceof ASTStringLiteral || it instanceof ASTLambdaExpression - || it instanceof ASTVariableAccess || it instanceof ASTMethodReference - || it instanceof ASTFieldAccess || it instanceof ASTThisExpression); + .all(GuardLogStatementRule::isDirectAccess); + } + + private static boolean isDirectAccess(ASTExpression it) { + return it instanceof ASTStringLiteral || it instanceof ASTLambdaExpression + || it instanceof ASTVariableAccess || it instanceof ASTMethodReference + || it instanceof ASTFieldAccess || it instanceof ASTThisExpression + || (it instanceof ASTArrayAccess && isDirectAccess(((ASTArrayAccess) it).getQualifier()) ); } private void extractProperties() { From 15b006a52f6dbd02a06a76b4580ea0850f470be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sun, 18 Aug 2024 19:41:56 -0300 Subject: [PATCH 41/58] Update changelog, refs #5153 --- docs/pages/release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 90b38429cc..e49d4e0965 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -20,6 +20,7 @@ This is a {{ site.pmd.release_type }} release. * java-bestpractices * [#5151](https://github.com/pmd/pmd/issues/5151): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is a constant from another class * [#5152](https://github.com/pmd/pmd/issues/5152): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is "this" + * [#5153](https://github.com/pmd/pmd/issues/5153): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is an array element * plsql-bestpractices * [#5132](https://github.com/pmd/pmd/issues/5132): \[plsql] TomKytesDespair - exception for more complex exception handler From 42468d769a2c1c055b7b9a3350cf70e29d0f8f2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2024 03:50:15 +0000 Subject: [PATCH 42/58] Bump net.bytebuddy:byte-buddy-agent from 1.14.12 to 1.14.19 Bumps [net.bytebuddy:byte-buddy-agent](https://github.com/raphw/byte-buddy) from 1.14.12 to 1.14.19. - [Release notes](https://github.com/raphw/byte-buddy/releases) - [Changelog](https://github.com/raphw/byte-buddy/blob/master/release-notes.md) - [Commits](https://github.com/raphw/byte-buddy/compare/byte-buddy-1.14.12...byte-buddy-1.14.19) --- updated-dependencies: - dependency-name: net.bytebuddy:byte-buddy-agent dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 39b335a742..87396b1377 100644 --- a/pom.xml +++ b/pom.xml @@ -988,7 +988,7 @@ net.bytebuddy byte-buddy-agent - 1.14.12 + 1.14.19 test From 90ebac893fb5913a0e7f52efb60e977c718b2197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Mon, 19 Aug 2024 01:45:37 -0300 Subject: [PATCH 43/58] Fix whitespace --- .../pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java index 43ff247174..33aa00dba2 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java @@ -247,7 +247,7 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { return it instanceof ASTStringLiteral || it instanceof ASTLambdaExpression || it instanceof ASTVariableAccess || it instanceof ASTMethodReference || it instanceof ASTFieldAccess || it instanceof ASTThisExpression - || (it instanceof ASTArrayAccess && isDirectAccess(((ASTArrayAccess) it).getQualifier()) ); + || (it instanceof ASTArrayAccess && isDirectAccess(((ASTArrayAccess) it).getQualifier())); } private void extractProperties() { From 4033315c7df4586cb1aed2a9ec8d45294f5c88b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Wed, 21 Aug 2024 23:40:04 -0300 Subject: [PATCH 44/58] Properly handle all qualifiable expressions - Up date the documentation to better show what is being flagged --- .../bestpractices/GuardLogStatementRule.java | 22 +++++-- .../resources/category/java/bestpractices.xml | 7 ++- .../bestpractices/xml/GuardLogStatement.xml | 61 +++++++++++++++++++ 3 files changed, 84 insertions(+), 6 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java index 33aa00dba2..bd7df95c64 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java @@ -23,13 +23,16 @@ import net.sourceforge.pmd.lang.java.ast.ASTFieldAccess; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression; +import net.sourceforge.pmd.lang.java.ast.ASTLiteral; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodReference; import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; import net.sourceforge.pmd.lang.java.ast.ASTThisExpression; +import net.sourceforge.pmd.lang.java.ast.ASTTypeExpression; import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; import net.sourceforge.pmd.lang.java.ast.ASTVariableId; import net.sourceforge.pmd.lang.java.ast.BinaryOp; +import net.sourceforge.pmd.lang.java.ast.QualifiableExpression; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; @@ -244,10 +247,21 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { } private static boolean isDirectAccess(ASTExpression it) { - return it instanceof ASTStringLiteral || it instanceof ASTLambdaExpression - || it instanceof ASTVariableAccess || it instanceof ASTMethodReference - || it instanceof ASTFieldAccess || it instanceof ASTThisExpression - || (it instanceof ASTArrayAccess && isDirectAccess(((ASTArrayAccess) it).getQualifier())); + final boolean isPermittedType = it instanceof ASTLiteral || it instanceof ASTLambdaExpression + || it instanceof ASTVariableAccess || it instanceof ASTThisExpression + || it instanceof ASTMethodReference || it instanceof ASTFieldAccess + || it instanceof ASTArrayAccess; + + if (!isPermittedType) { + return false; + } + + if (it instanceof QualifiableExpression) { + final ASTExpression qualifier = ((QualifiableExpression) it).getQualifier(); + return qualifier == null || qualifier instanceof ASTTypeExpression || isDirectAccess(qualifier); + } + + return true; } private void extractProperties() { diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index a8a3169254..39e3d93946 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -603,7 +603,7 @@ for (int i = 0, j = 0; i < 10; i++, j += 2) { externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#guardlogstatement"> Whenever using a log level, one should check if the loglevel is actually enabled, or -otherwise skip the associate String creation and manipulation. +otherwise skip the associate String creation and manipulation, as well as any expensive method calls. An alternative to checking the log level are substituting parameters, formatters or lazy logging with lambdas. The available alternatives depend on the actual logging framework. @@ -611,7 +611,7 @@ with lambdas. The available alternatives depend on the actual logging framework. 2 calculateExpensiveLoggingText()); ]]> diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml index f2f91314b8..519e7ea958 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml @@ -582,6 +582,31 @@ public class GuardLogStatementTest { ]]> + + GuardLogStatement should flag method reference from method / constructor calls + 2 + + + #5151 Field accesses do not require guards 0 @@ -592,12 +617,48 @@ import org.apache.logging.log4j.LogManager; public class GuardLogStatementTest { private static final Logger LOG = LogManager.getLogger(GuardLogStatementTest.class); private static final String MY_CONSTANT = ""; + private String my_field = ""; public void test() { LOG.info( "Some message here : foo={}", GuardLogStatementTest.MY_CONSTANT ); + + LOG.info( + "Some message here : foo={}", + this.my_field + ); + } +} + ]]> + + + + #5151 Field accesses on method call returned values require guards + 2 + From 43112078a54501b2f793423938c38d84ec5fa51e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Fri, 23 Aug 2024 18:19:45 +0200 Subject: [PATCH 45/58] Fix issue #5145 - problem with inference of conditional exprs Also finally fixes the bug that we swept under the rug in 24c1093b. --- .../internal/infer/ast/BaseInvocMirror.java | 19 ++++++++-- .../infer/ast/ConditionalMirrorImpl.java | 19 +--------- .../internal/infer/ast/CtorInvocMirror.java | 11 ++++-- .../internal/infer/ast/JavaExprMirrors.java | 19 ++++++---- .../internal/infer/ast/MethodInvocMirror.java | 8 ++-- .../internal/infer/BranchingExprsTests.kt | 2 +- .../internal/infer/LocalVarInferenceTest.kt | 38 ++++++++++++++++++- 7 files changed, 78 insertions(+), 38 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/BaseInvocMirror.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/BaseInvocMirror.java index d497c0801b..12fabedf2c 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/BaseInvocMirror.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/BaseInvocMirror.java @@ -31,9 +31,17 @@ abstract class BaseInvocMirror extends BasePolyMirror< private MethodCtDecl ctDecl; private List args; + /** + * Some method invocations may appear to be poly expressions, + * but they have no context type (for instance because they + * are in the initializer of a local with inferred type). + * These must be treated as standalone expressions. + */ + protected final boolean mayBePoly; - BaseInvocMirror(JavaExprMirrors mirrors, T call, @Nullable ExprMirror parent, MirrorMaker subexprMaker) { + BaseInvocMirror(JavaExprMirrors mirrors, T call, boolean mustBeStandalone, @Nullable ExprMirror parent, MirrorMaker subexprMaker) { super(mirrors, call, parent, subexprMaker); + mayBePoly = !mustBeStandalone; } @Override @@ -61,8 +69,13 @@ abstract class BaseInvocMirror extends BasePolyMirror< protected MethodCtDecl getStandaloneCtdecl() { MethodCallSite site = factory.infer.newCallSite(this, null); - // this is cached for later anyway - return factory.infer.getCompileTimeDecl(site); + if (mayBePoly) { + // this is cached for later anyway + return factory.infer.getCompileTimeDecl(site); + } else { + factory.infer.inferInvocationRecursively(site); + return site.getExpr().getCtDecl(); + } } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/ConditionalMirrorImpl.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/ConditionalMirrorImpl.java index f0f5217fb6..2c236580e3 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/ConditionalMirrorImpl.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/ConditionalMirrorImpl.java @@ -19,7 +19,6 @@ import net.sourceforge.pmd.lang.java.types.JTypeMirror; import net.sourceforge.pmd.lang.java.types.TypeConversion; import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror; import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.BranchingMirror; -import net.sourceforge.pmd.lang.java.types.internal.infer.MethodCallSite; import net.sourceforge.pmd.lang.java.types.internal.infer.ast.JavaExprMirrors.MirrorMaker; class ConditionalMirrorImpl extends BasePolyMirror implements BranchingMirror { @@ -135,23 +134,7 @@ class ConditionalMirrorImpl extends BasePolyMirror imp } if (e instanceof ASTMethodCall) { - /* - A method invocation expression (ยง15.12) for which the chosen most specific method (ยง15.12.2.5) has return type boolean or Boolean. - Note that, for a generic method, this is the type before instantiating the method's type arguments. - - */ - JTypeMirror current = InternalApiBridge.getTypeMirrorInternal(e); - if (current != null) { - // don't redo the compile-time decl resolution - // The CTDecl is cached on the mirror, not the node - return current; - } - - MethodCallSite site = factory.infer.newCallSite((InvocationMirror) mirror, null); - - return factory.infer.getCompileTimeDecl(site) - .getMethodType() - .getReturnType(); + return mirror.getStandaloneType(); } return null; diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/CtorInvocMirror.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/CtorInvocMirror.java index 5aea093a0e..bb9167a335 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/CtorInvocMirror.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/CtorInvocMirror.java @@ -29,8 +29,9 @@ import net.sourceforge.pmd.util.IteratorUtil; class CtorInvocMirror extends BaseInvocMirror implements CtorInvocationMirror { - CtorInvocMirror(JavaExprMirrors mirrors, ASTConstructorCall call, ExprMirror parent, MirrorMaker subexprMaker) { - super(mirrors, call, parent, subexprMaker); + CtorInvocMirror(JavaExprMirrors mirrors, ASTConstructorCall call, + boolean mustBeStandalone, ExprMirror parent, MirrorMaker subexprMaker) { + super(mirrors, call, mustBeStandalone, parent, subexprMaker); } @Override @@ -47,6 +48,8 @@ class CtorInvocMirror extends BaseInvocMirror implements Cto @Override public JTypeMirror getStandaloneType() { if (isDiamond()) { + // todo if the expr must be standalone then we + // should infer this from the provided arguments. return null; } return getNewType(); @@ -130,7 +133,7 @@ class CtorInvocMirror extends BaseInvocMirror implements Cto EnumCtorInvocMirror(JavaExprMirrors mirrors, ASTEnumConstant call, ExprMirror parent, MirrorMaker subexprMaker) { - super(mirrors, call, parent, subexprMaker); + super(mirrors, call, false, parent, subexprMaker); } @Override @@ -163,7 +166,7 @@ class CtorInvocMirror extends BaseInvocMirror implements Cto ExplicitCtorInvocMirror(JavaExprMirrors mirrors, ASTExplicitConstructorInvocation call, ExprMirror parent, MirrorMaker subexprMaker) { - super(mirrors, call, parent, subexprMaker); + super(mirrors, call, false, parent, subexprMaker); } @Override diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/JavaExprMirrors.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/JavaExprMirrors.java index eb8e57b3e1..8287afec35 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/JavaExprMirrors.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/JavaExprMirrors.java @@ -68,7 +68,7 @@ public final class JavaExprMirrors { ExprMirror makeSubexprDefault(ASTExpression e, @Nullable ExprMirror parent, MirrorMaker subexprMaker) { if (e instanceof InvocationNode) { - return getInvocationMirror((InvocationNode) e, parent, subexprMaker); + return getInvocationMirror((InvocationNode) e, parent, false, subexprMaker); } else if (e instanceof ASTLambdaExpression || e instanceof ASTMethodReference) { return getFunctionalMirror(e, parent, subexprMaker); } else if (e instanceof ASTConditionalExpression) { @@ -81,11 +81,13 @@ public final class JavaExprMirrors { } } - ExprMirror getBranchMirrorSubexpression(ASTExpression e, boolean isStandalone, @NonNull BranchingMirror parent, MirrorMaker subexprMaker) { + ExprMirror getBranchMirrorSubexpression(ASTExpression e, boolean mustBeStandalone, @NonNull BranchingMirror parent, MirrorMaker subexprMaker) { if (e instanceof ASTConditionalExpression) { - return new ConditionalMirrorImpl(this, (ASTConditionalExpression) e, isStandalone, parent, subexprMaker); + return new ConditionalMirrorImpl(this, (ASTConditionalExpression) e, mustBeStandalone, parent, subexprMaker); } else if (e instanceof ASTSwitchExpression) { - return new SwitchMirror(this, (ASTSwitchExpression) e, isStandalone, parent, subexprMaker); + return new SwitchMirror(this, (ASTSwitchExpression) e, mustBeStandalone, parent, subexprMaker); + } else if (e instanceof InvocationNode) { + return getInvocationMirror((InvocationNode) e, parent, mustBeStandalone, subexprMaker); } else { return subexprMaker.createMirrorForSubexpression(e, parent, subexprMaker); } @@ -96,14 +98,15 @@ public final class JavaExprMirrors { } public InvocationMirror getInvocationMirror(InvocationNode e, MirrorMaker subexprMaker) { - return getInvocationMirror(e, null, subexprMaker); + return getInvocationMirror(e, null, false, subexprMaker); } - private InvocationMirror getInvocationMirror(InvocationNode e, @Nullable ExprMirror parent, MirrorMaker subexprMaker) { + private InvocationMirror getInvocationMirror(InvocationNode e, @Nullable ExprMirror parent, + boolean mustBeStandalone, MirrorMaker subexprMaker) { if (e instanceof ASTMethodCall) { - return new MethodInvocMirror(this, (ASTMethodCall) e, parent, subexprMaker); + return new MethodInvocMirror(this, (ASTMethodCall) e, mustBeStandalone, parent, subexprMaker); } else if (e instanceof ASTConstructorCall) { - return new CtorInvocMirror(this, (ASTConstructorCall) e, parent, subexprMaker); + return new CtorInvocMirror(this, (ASTConstructorCall) e, mustBeStandalone, parent, subexprMaker); } else if (e instanceof ASTExplicitConstructorInvocation) { return new CtorInvocMirror.ExplicitCtorInvocMirror(this, (ASTExplicitConstructorInvocation) e, parent, subexprMaker); } else if (e instanceof ASTEnumConstant) { diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/MethodInvocMirror.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/MethodInvocMirror.java index fd66d5116f..3404147596 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/MethodInvocMirror.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/internal/infer/ast/MethodInvocMirror.java @@ -26,14 +26,16 @@ import net.sourceforge.pmd.lang.java.types.internal.infer.ast.JavaExprMirrors.Mi class MethodInvocMirror extends BaseInvocMirror { - MethodInvocMirror(JavaExprMirrors mirrors, ASTMethodCall call, @Nullable ExprMirror parent, MirrorMaker subexprMaker) { - super(mirrors, call, parent, subexprMaker); + MethodInvocMirror(JavaExprMirrors mirrors, ASTMethodCall call, + boolean isStandalone, + @Nullable ExprMirror parent, MirrorMaker subexprMaker) { + super(mirrors, call, isStandalone, parent, subexprMaker); } @Override public @Nullable JTypeMirror getStandaloneType() { JMethodSig ctdecl = getStandaloneCtdecl().getMethodType(); - return isContextDependent(ctdecl) ? null : ctdecl.getReturnType(); + return mayBePoly && isContextDependent(ctdecl) ? null : ctdecl.getReturnType(); } private static boolean isContextDependent(JMethodSig m) { diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/BranchingExprsTests.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/BranchingExprsTests.kt index 51eb5943df..756df350fb 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/BranchingExprsTests.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/BranchingExprsTests.kt @@ -279,7 +279,7 @@ class Scratch { val (ternary1, ternary2) = acu.descendants(ASTConditionalExpression::class.java).toList() spy.shouldBeOk { - ternary1 shouldHaveType gen.t_Collection[captureMatcher(`?`)] // java.util.Collection + ternary1 shouldHaveType gen.t_Collection[ts.OBJECT] // java.util.Collection ternary2 shouldHaveType gen.`t_Collection{String}` // java.util.Collection } } diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/LocalVarInferenceTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/LocalVarInferenceTest.kt index 268b973d29..6b1e1c327d 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/LocalVarInferenceTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/internal/infer/LocalVarInferenceTest.kt @@ -4,10 +4,11 @@ package net.sourceforge.pmd.lang.java.types.internal.infer -import net.sourceforge.pmd.lang.test.ast.shouldMatchN import net.sourceforge.pmd.lang.java.ast.ProcessorTestSpec import net.sourceforge.pmd.lang.java.ast.variableAccess import net.sourceforge.pmd.lang.java.types.* +import net.sourceforge.pmd.lang.test.ast.component6 +import net.sourceforge.pmd.lang.test.ast.shouldMatchN /** * @@ -101,4 +102,39 @@ class Scratch { acu.varId("k") shouldHaveType Runnable::class.decl } } + parserTest("Local var with conditional and 2 poly expressions") { + val (acu, spy) = parser.parseWithTypeInferenceSpy( + """ +interface Collector {} +interface Stream { + R collect(Collector collector); +} +interface List {} +interface Function { R apply(T t); } +public class Scratch { + + static Collector>> groupingByNullable(Function key){} + static { + var lookup = System.currentTimeMillis() > 100L // permission based + ? Dao.source1().collect(groupingByNullable(Data::parent)) // DAO call #1 + : Dao.source2().collect(groupingByNullable(Data::parent)); // DAO call #2 + } + + interface Data { + Integer parent(); + } + static class Dao { + static Stream source1(){} + static Stream source2(){} + } +} + """.trimIndent() + ) + + spy.shouldBeOk { + val (_, _, list, _, _, data) = acu.declaredTypeSignatures() + // not the anon type + acu.varId("lookup") shouldHaveType list[list[data]] + } + } }) From 218222524f931cd5eae226be56d7300651bd6fa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Fri, 23 Aug 2024 14:36:12 -0300 Subject: [PATCH 46/58] Update changelog, refs #5145 --- docs/pages/release_notes.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 39aef42caa..c12e6be840 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -17,6 +17,8 @@ This is a {{ site.pmd.release_type }} release. ### ๐Ÿ› Fixed Issues * apex-performance * [#5139](https://github.com/pmd/pmd/issues/5139): \[apex] OperationWithHighCostInLoop not firing in triggers +* pmd-bestpractices + * [#5145](https://github.com/pmd/pmd/issues/5145): \[java] False positive UnusedPrivateMethod * plsql-bestpractices * [#5132](https://github.com/pmd/pmd/issues/5132): \[plsql] TomKytesDespair - exception for more complex exception handler From 96f60e9b0e990533e4b50a7254a583bc6e76413e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sat, 24 Aug 2024 00:11:43 -0300 Subject: [PATCH 47/58] Handle computed array access keys - Tidy up the code, the fact the `getLogLevelName` was cheating and returning null when it considered a log to be safe was making the code harder to think about --- .../bestpractices/GuardLogStatementRule.java | 56 ++++++++++--------- .../bestpractices/xml/GuardLogStatement.xml | 25 +++++++++ 2 files changed, 54 insertions(+), 27 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java index bd7df95c64..eaae87d7bc 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java @@ -14,7 +14,6 @@ import java.util.Map; import org.checkerframework.checker.nullness.qual.Nullable; -import net.sourceforge.pmd.lang.java.ast.ASTArgumentList; import net.sourceforge.pmd.lang.java.ast.ASTArrayAccess; import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; import net.sourceforge.pmd.lang.java.ast.ASTExpression; @@ -31,7 +30,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTThisExpression; import net.sourceforge.pmd.lang.java.ast.ASTTypeExpression; import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; import net.sourceforge.pmd.lang.java.ast.ASTVariableId; -import net.sourceforge.pmd.lang.java.ast.BinaryOp; import net.sourceforge.pmd.lang.java.ast.QualifiableExpression; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; @@ -120,23 +118,21 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { } private boolean needsGuard(ASTMethodCall node) { - if (node.getArguments().size() == 0) { + if (node.getArguments().isEmpty()) { return false; } - ASTArgumentList argumentList = node.getArguments(); - for (ASTExpression child : argumentList) { - if (child.descendantsOrSelf() - .filterIs(ASTInfixExpression.class) - .filter(n -> n.getOperator() == BinaryOp.ADD) - .nonEmpty() - && TypeTestUtil.isA(String.class, child)) { - // only consider the first String argument - which is the log message - and return here - return !isConstantStringExpression(child); - } + // get the message expression + // it must either be a direct access (var / param access, lambda, method ref, etc.) + // or a compile-time constant string to not require a guard + int messageArg = getMessageArgIndex(node); + ASTExpression messageExpr = node.getArguments().get(messageArg); + if (!isDirectAccess(messageExpr) && !isConstantStringExpression(messageExpr)) { + return true; } - return true; + // if any additional params are not a direct access, we need a guard + return !areAdditionalParamsDirectAccess(node, messageArg + 1); } private boolean isConstantStringExpression(ASTExpression expr) { @@ -169,10 +165,8 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { if (expr instanceof ASTInfixExpression) { ASTInfixExpression infix = (ASTInfixExpression) expr; - if (isConstantStringExpression(infix.getLeftOperand()) - && isConstantStringExpression(infix.getRightOperand())) { - return true; - } + return isConstantStringExpression(infix.getLeftOperand()) + && isConstantStringExpression(infix.getRightOperand()); } return false; } @@ -216,20 +210,22 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { private @Nullable String getLogLevelName(ASTMethodCall methodCall) { String methodName = methodCall.getMethodName(); if (!JAVA_UTIL_LOG_METHOD.equals(methodName)) { - if (isUnguardedAccessOk(methodCall, 0)) { - return null; - } return methodName; // probably logger.warn(...) } - // else it's java.util.logging, eg - // LOGGER.log(Level.FINE, "m") - if (isUnguardedAccessOk(methodCall, 1)) { - return null; - } return getJutilLogLevelInFirstArg(methodCall); } + private int getMessageArgIndex(ASTMethodCall methodCall) { + String methodName = methodCall.getMethodName(); + if (JAVA_UTIL_LOG_METHOD.equals(methodName)) { + // LOGGER.log(Level.FINE, "m") + return 1; + } + + return 0; + } + private @Nullable String getJutilLogLevelInFirstArg(ASTMethodCall methodCall) { ASTExpression firstArg = methodCall.getArguments().toStream().get(0); if (TypeTestUtil.isA("java.util.logging.Level", firstArg) && firstArg instanceof ASTNamedReferenceExpr) { @@ -238,7 +234,7 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { return null; } - private boolean isUnguardedAccessOk(ASTMethodCall call, int messageArgIndex) { + private boolean areAdditionalParamsDirectAccess(ASTMethodCall call, int messageArgIndex) { // return true if the statement has limited overhead even if unguarded, // so that we can ignore it return call.getArguments().toStream() @@ -258,6 +254,12 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { if (it instanceof QualifiableExpression) { final ASTExpression qualifier = ((QualifiableExpression) it).getQualifier(); + + // for array access, we also care about the index expression + if (it instanceof ASTArrayAccess && !isDirectAccess(((ASTArrayAccess) it).getIndexExpression())) { + return false; + } + return qualifier == null || qualifier instanceof ASTTypeExpression || isDirectAccess(qualifier); } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml index 519e7ea958..7ad9e52576 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml @@ -705,6 +705,31 @@ public class GuardLogStatementTest { ]]> + + array access with computed keys require guards + 1 + + + #5153 array access to method returned values require guards 1 From 980eaacd13b1eb13b79c4bf3edb19e9cdce17fff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sat, 24 Aug 2024 00:48:53 -0300 Subject: [PATCH 48/58] Unify compile-time constant detection - Improving the constant folder allows other rules to work better - Fixes #3602 - Curiously, InefficientStringBuffering has been broken since PMD 7.0.0 (regression introduced in #3113) --- .../pmd/lang/java/ast/ConstantFolder.java | 26 ++++++++---- .../bestpractices/GuardLogStatementRule.java | 42 +------------------ .../bestpractices/GuardLogStatementTest.java | 2 + .../bestpractices/xml/GuardLogStatement.xml | 18 ++++++++ .../xml/InefficientStringBuffering.xml | 11 ++--- 5 files changed, 41 insertions(+), 58 deletions(-) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ConstantFolder.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ConstantFolder.java index eb3c7f1770..5079ee9223 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ConstantFolder.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/ast/ConstantFolder.java @@ -9,7 +9,6 @@ import org.apache.commons.lang3.tuple.Pair; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr; import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol; import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; import net.sourceforge.pmd.lang.java.types.JPrimitiveType; @@ -42,18 +41,27 @@ final strictfp class ConstantFolder extends JavaVisitorBase { @Override public Object visit(ASTVariableAccess node, Void data) { - return fetchConstFieldReference(node); + JVariableSymbol symbol = node.getReferencedSym(); + if (symbol == null || !symbol.isFinal()) { + return null; + } + @Nullable + ASTVariableId declaratorId = symbol.tryGetNode(); + if (declaratorId != null) { + ASTExpression initializer = declaratorId.getInitializer(); + if (initializer != null) { + return initializer.getConstValue(); + } + } + + return null; } @Override public Object visit(ASTFieldAccess node, Void data) { - return fetchConstFieldReference(node); - } - - private @Nullable Object fetchConstFieldReference(ASTNamedReferenceExpr node) { - JVariableSymbol symbol = node.getReferencedSym(); - if (symbol instanceof JFieldSymbol) { - return ((JFieldSymbol) symbol).getConstValue(); + JFieldSymbol symbol = node.getReferencedSym(); + if (symbol != null) { + return symbol.getConstValue(); } return null; } diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java index eaae87d7bc..117efcd008 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java @@ -20,19 +20,15 @@ import net.sourceforge.pmd.lang.java.ast.ASTExpression; import net.sourceforge.pmd.lang.java.ast.ASTExpressionStatement; import net.sourceforge.pmd.lang.java.ast.ASTFieldAccess; import net.sourceforge.pmd.lang.java.ast.ASTIfStatement; -import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression; import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression; import net.sourceforge.pmd.lang.java.ast.ASTLiteral; import net.sourceforge.pmd.lang.java.ast.ASTMethodCall; import net.sourceforge.pmd.lang.java.ast.ASTMethodReference; -import net.sourceforge.pmd.lang.java.ast.ASTStringLiteral; import net.sourceforge.pmd.lang.java.ast.ASTThisExpression; import net.sourceforge.pmd.lang.java.ast.ASTTypeExpression; import net.sourceforge.pmd.lang.java.ast.ASTVariableAccess; -import net.sourceforge.pmd.lang.java.ast.ASTVariableId; import net.sourceforge.pmd.lang.java.ast.QualifiableExpression; import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule; -import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol; import net.sourceforge.pmd.lang.java.types.TypeTestUtil; import net.sourceforge.pmd.properties.PropertyDescriptor; import net.sourceforge.pmd.reporting.RuleContext; @@ -127,7 +123,7 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { // or a compile-time constant string to not require a guard int messageArg = getMessageArgIndex(node); ASTExpression messageExpr = node.getArguments().get(messageArg); - if (!isDirectAccess(messageExpr) && !isConstantStringExpression(messageExpr)) { + if (!isDirectAccess(messageExpr) && !messageExpr.isCompileTimeConstant()) { return true; } @@ -135,42 +131,6 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { return !areAdditionalParamsDirectAccess(node, messageArg + 1); } - private boolean isConstantStringExpression(ASTExpression expr) { - if (expr == null) { - return false; - } - - if (expr instanceof ASTStringLiteral) { - return true; - } - - if (expr instanceof ASTVariableAccess) { - ASTVariableAccess var = (ASTVariableAccess) expr; - if (var.isCompileTimeConstant()) { - return true; - } - JVariableSymbol symbol = var.getReferencedSym(); - if (symbol == null) { - return false; - } - if (!var.getReferencedSym().isFinal()) { - return false; - } - @Nullable - ASTVariableId declaratorId = symbol.tryGetNode(); - if (declaratorId != null) { - return isConstantStringExpression(declaratorId.getInitializer()); - } - } - - if (expr instanceof ASTInfixExpression) { - ASTInfixExpression infix = (ASTInfixExpression) expr; - return isConstantStringExpression(infix.getLeftOperand()) - && isConstantStringExpression(infix.getRightOperand()); - } - return false; - } - private boolean hasGuard(ASTMethodCall node, String logLevel) { ASTIfStatement ifStatement = node.ancestors(ASTIfStatement.class).first(); if (ifStatement == null) { diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementTest.java index 5a1979c853..a4f1432202 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementTest.java @@ -8,4 +8,6 @@ import net.sourceforge.pmd.test.PmdRuleTst; class GuardLogStatementTest extends PmdRuleTst { // no additional unit tests + + public static final String TERM_MSG = "A terminating log message."; } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml index 7ad9e52576..4c8970291d 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/bestpractices/xml/GuardLogStatement.xml @@ -750,6 +750,24 @@ public class GuardLogStatementTest { private String[] foo() { return new String[] { "foo" }; } +} + ]]> + + + + #3602 False positive when compile-time constant is created from external constants + 0 + diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientStringBuffering.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientStringBuffering.xml index faaebfad2b..c6cd35430c 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientStringBuffering.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/performance/xml/InefficientStringBuffering.xml @@ -393,8 +393,7 @@ public class Foo { No violation: Avoid concat in append method invocations - 3 - 26,39,45 + 0 From 1c274fdda0a03f95f3617dc41e2eeff23ed1d9c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sat, 24 Aug 2024 00:51:15 -0300 Subject: [PATCH 49/58] Update changelog, refs #3602 --- docs/pages/release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 9332e42579..5dea24af93 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,6 +18,7 @@ This is a {{ site.pmd.release_type }} release. * apex-performance * [#5139](https://github.com/pmd/pmd/issues/5139): \[apex] OperationWithHighCostInLoop not firing in triggers * java-bestpractices + * [#3602](https://github.com/pmd/pmd/issues/3602): \[java] GuardLogStatement: False positive when compile-time constant is created from external constants * [#5145](https://github.com/pmd/pmd/issues/5145): \[java] False positive UnusedPrivateMethod * [#5151](https://github.com/pmd/pmd/issues/5151): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is a constant from another class * [#5152](https://github.com/pmd/pmd/issues/5152): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is "this" From c9c0558e2baea2807fa6e3c406d1067cdf9d7843 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sat, 24 Aug 2024 01:02:06 -0300 Subject: [PATCH 50/58] Suppress PMD warning - "Simplifying" the return here would simply make it harder to read --- .../pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java | 1 + 1 file changed, 1 insertion(+) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java index 117efcd008..9069831804 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/bestpractices/GuardLogStatementRule.java @@ -113,6 +113,7 @@ public class GuardLogStatementRule extends AbstractJavaRulechainRule { return null; } + @SuppressWarnings("PMD.SimplifyBooleanReturns") private boolean needsGuard(ASTMethodCall node) { if (node.getArguments().isEmpty()) { return false; From da864aeccd0f19c4494eabbca8610cf26075a2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Fournier?= Date: Sat, 24 Aug 2024 16:48:03 +0200 Subject: [PATCH 51/58] Fix #5167 - issue with type projection --- .../pmd/lang/java/types/TypeOps.java | 7 ++++ .../pmd/lang/java/types/TypeOpsTest.kt | 37 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java index 051ea91c02..a6ac882819 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeOps.java @@ -1170,6 +1170,13 @@ public final class TypeOps { } else if (!upwards) { // If Ai is a type that mentions a restricted type variable, then Ai' is undefined. return NO_DOWN_PROJECTION; + } else if (u instanceof JWildcardType) { + // The rest of this function, below, treats u as the bound of a wildcard, + // but if u is already a wildcard (and therefore ai was a wildcard), we + // are already done. + newTargs.add(u); + change = true; + continue; } change = true; diff --git a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TypeOpsTest.kt b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TypeOpsTest.kt index 55a1b19a82..421a778c4a 100644 --- a/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TypeOpsTest.kt +++ b/pmd-java/src/test/kotlin/net/sourceforge/pmd/lang/java/types/TypeOpsTest.kt @@ -95,7 +95,44 @@ class TypeOpsTest : FunSpec({ } + test("#5167 problem in projection") { + val (acu, spy) = javaParser.parseWithTypeInferenceSpy( + """ +import java.lang.annotation.Annotation; +interface Bar { + Baz getBaz(); +} + +interface Predicate { + boolean check(T t); +} +interface Stream{ + T findSome(); +} +interface Baz{ + Stream> filterMethods(Predicate p); +} + +class Foo { + + private static Bar foo( + Bar type, Class annotation, boolean required) { + var method = type.getBaz().filterMethods(m -> true).findSome(); + return method; + } +} + """.trimIndent() + ) + + val (barT) = acu.declaredTypeSignatures() + val methodId = acu.varId("method") + + spy.shouldBeOk { + methodId shouldHaveType barT[`?`] + } + } } } + }) From be47aab26543d40c31a9713df4baf60abf0bb1db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sat, 24 Aug 2024 18:18:38 -0300 Subject: [PATCH 52/58] Update changelog, refs #5167 --- docs/pages/release_notes.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index c12e6be840..1bc6f6fc00 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -17,7 +17,9 @@ This is a {{ site.pmd.release_type }} release. ### ๐Ÿ› Fixed Issues * apex-performance * [#5139](https://github.com/pmd/pmd/issues/5139): \[apex] OperationWithHighCostInLoop not firing in triggers -* pmd-bestpractices +* java + * [#5167](https://github.com/pmd/pmd/pull/5167): \[java] java.lang.IllegalArgumentException: \<\?\> cannot be a wildcard bound +* java-bestpractices * [#5145](https://github.com/pmd/pmd/issues/5145): \[java] False positive UnusedPrivateMethod * plsql-bestpractices * [#5132](https://github.com/pmd/pmd/issues/5132): \[plsql] TomKytesDespair - exception for more complex exception handler From 91bb6f0b34370249236d6b964ebc656660ee9a0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sat, 24 Aug 2024 23:44:22 -0300 Subject: [PATCH 53/58] Improve docs further --- .../src/main/resources/category/java/bestpractices.xml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pmd-java/src/main/resources/category/java/bestpractices.xml b/pmd-java/src/main/resources/category/java/bestpractices.xml index 39e3d93946..4bf4a370bf 100644 --- a/pmd-java/src/main/resources/category/java/bestpractices.xml +++ b/pmd-java/src/main/resources/category/java/bestpractices.xml @@ -602,8 +602,8 @@ for (int i = 0, j = 0; i < 10; i++, j += 2) { class="net.sourceforge.pmd.lang.java.rule.bestpractices.GuardLogStatementRule" externalInfoUrl="${pmd.website.baseurl}/pmd_rules_java_bestpractices.html#guardlogstatement"> -Whenever using a log level, one should check if the loglevel is actually enabled, or -otherwise skip the associate String creation and manipulation, as well as any expensive method calls. +Whenever using a log level, one should check if it is actually enabled, or +otherwise skip the associate String creation and manipulation, as well as any method calls. An alternative to checking the log level are substituting parameters, formatters or lazy logging with lambdas. The available alternatives depend on the actual logging framework. @@ -627,6 +627,9 @@ log.debug("log something expensive: {}", calculateExpensiveLoggingText()); // Avoid the guarding if statement with lazy logging and lambdas log.debug("log something expensive: {}", () -> calculateExpensiveLoggingText()); + +// โ€ฆ alternatively use method references +log.debug("log something expensive: {}", this::calculateExpensiveLoggingText); ]]> From 2f118d0d461f97bce3217345a9cc85a7f143d072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Mart=C3=ADn=20Sotuyo=20Dodero?= Date: Sat, 24 Aug 2024 23:46:33 -0300 Subject: [PATCH 54/58] Update changelog, refs #4731 --- docs/pages/release_notes.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 592acc87d0..9790230448 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -21,6 +21,7 @@ This is a {{ site.pmd.release_type }} release. * [#5167](https://github.com/pmd/pmd/pull/5167): \[java] java.lang.IllegalArgumentException: \<\?\> cannot be a wildcard bound * java-bestpractices * [#3602](https://github.com/pmd/pmd/issues/3602): \[java] GuardLogStatement: False positive when compile-time constant is created from external constants + * [#4731](https://github.com/pmd/pmd/issues/4731): \[java] GuardLogStatement documentation is unclear why getters are flagged * [#5145](https://github.com/pmd/pmd/issues/5145): \[java] False positive UnusedPrivateMethod * [#5151](https://github.com/pmd/pmd/issues/5151): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is a constant from another class * [#5152](https://github.com/pmd/pmd/issues/5152): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is "this" From 77bf2b1616ab2092a9deb795cccfb361a0136c56 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Tue, 27 Aug 2024 19:13:56 +0200 Subject: [PATCH 55/58] [doc] Update release notes (#5167) --- docs/pages/release_notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index 1bc6f6fc00..a65d047c85 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -18,7 +18,7 @@ This is a {{ site.pmd.release_type }} release. * apex-performance * [#5139](https://github.com/pmd/pmd/issues/5139): \[apex] OperationWithHighCostInLoop not firing in triggers * java - * [#5167](https://github.com/pmd/pmd/pull/5167): \[java] java.lang.IllegalArgumentException: \<\?\> cannot be a wildcard bound + * [#5167](https://github.com/pmd/pmd/issues/5167): \[java] java.lang.IllegalArgumentException: \ cannot be a wildcard bound * java-bestpractices * [#5145](https://github.com/pmd/pmd/issues/5145): \[java] False positive UnusedPrivateMethod * plsql-bestpractices From cd33eb621e4ab0b5a0d8a21f73fc3c1fc51e1b10 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Tue, 27 Aug 2024 19:16:38 +0200 Subject: [PATCH 56/58] Update gems Fixes https://github.com/pmd/pmd/security/dependabot/64 Fixes CVE-2024-43380 Fixes https://github.com/advisories/GHSA-2m96-52r3-2f3g Fixes https://github.com/pmd/pmd/security/dependabot/65 Fixes https://github.com/pmd/pmd/security/dependabot/66 Fixes CVE-2024-43398 Fixes https://github.com/advisories/GHSA-vmwr-mc7x-5vc3 --- Gemfile.lock | 18 +++++++++--------- docs/Gemfile.lock | 27 ++++++++++++++------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 3fdfbb4ca7..9a7014556a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,7 +10,7 @@ GEM nap open4 (~> 1.3) colored2 (3.1.2) - concurrent-ruby (1.3.3) + concurrent-ruby (1.3.4) cork (0.3.0) colored2 (~> 3.1) danger (9.5.0) @@ -28,14 +28,14 @@ GEM differ (0.1.2) et-orbi (1.2.11) tzinfo - faraday (2.10.1) - faraday-net_http (>= 2.0, < 3.2) + faraday (2.11.0) + faraday-net_http (>= 2.0, < 3.4) logger faraday-http-cache (2.5.1) faraday (>= 0.8) - faraday-net_http (3.1.1) + faraday-net_http (3.3.0) net-http - fugit (1.11.0) + fugit (1.11.1) et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) git (1.19.1) @@ -51,7 +51,7 @@ GEM nap (1.1.0) net-http (0.4.1) uri - nokogiri (1.16.6-x86_64-linux) + nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) octokit (9.1.0) faraday (>= 1, < 3) @@ -66,9 +66,9 @@ GEM slop (~> 4.9) public_suffix (6.0.1) raabro (1.4.0) - racc (1.8.0) + racc (1.8.1) rchardet (1.8.0) - rexml (3.3.4) + rexml (3.3.6) strscan rouge (4.3.0) rufus-scheduler (3.9.1) @@ -84,7 +84,7 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.5.0) - uri (0.13.0) + uri (0.13.1) PLATFORMS x86_64-linux diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index 5901351a72..3f44ef4782 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -1,16 +1,17 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.1.3.4) + activesupport (7.2.1) base64 bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - mutex_m - tzinfo (~> 2.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) base64 (0.2.0) @@ -34,10 +35,10 @@ GEM ffi (>= 1.15.0) eventmachine (1.2.7) execjs (2.9.1) - faraday (2.10.0) - faraday-net_http (>= 2.0, < 3.2) + faraday (2.11.0) + faraday-net_http (>= 2.0, < 3.4) logger - faraday-net_http (3.1.0) + faraday-net_http (3.3.0) net-http ffi (1.17.0-x86_64-linux-gnu) forwardable-extended (2.6.0) @@ -224,11 +225,10 @@ GEM jekyll (>= 3.5, < 5.0) jekyll-feed (~> 0.9) jekyll-seo-tag (~> 2.1) - minitest (5.24.1) - mutex_m (0.2.0) + minitest (5.25.1) net-http (0.4.1) uri - nokogiri (1.16.6-x86_64-linux) + nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) octokit (4.25.1) faraday (>= 1, < 3) @@ -236,11 +236,11 @@ GEM pathutil (0.16.2) forwardable-extended (~> 2.6) public_suffix (5.1.1) - racc (1.8.0) + racc (1.8.1) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rexml (3.3.5) + rexml (3.3.6) strscan rouge (3.30.0) rubyzip (2.3.2) @@ -253,6 +253,7 @@ GEM sawyer (0.9.2) addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) + securerandom (0.3.1) simpleidn (0.2.3) strscan (3.1.0) terminal-table (1.8.0) @@ -262,7 +263,7 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (1.8.0) - uri (0.13.0) + uri (0.13.1) webrick (1.8.1) PLATFORMS From af1929318bd39cf8c3b8776a99b5faa379dfecfc Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 29 Aug 2024 09:30:40 +0200 Subject: [PATCH 57/58] [doc] Update release notes --- docs/pages/release_notes.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index ff40928d45..af66a0f357 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -16,18 +16,18 @@ This is a {{ site.pmd.release_type }} release. ### ๐Ÿ› Fixed Issues * apex-performance - * [#5139](https://github.com/pmd/pmd/issues/5139): \[apex] OperationWithHighCostInLoop not firing in triggers + * [#5139](https://github.com/pmd/pmd/issues/5139): \[apex] OperationWithHighCostInLoop: false negative for triggers * java * [#5167](https://github.com/pmd/pmd/issues/5167): \[java] java.lang.IllegalArgumentException: \ cannot be a wildcard bound * java-bestpractices * [#3602](https://github.com/pmd/pmd/issues/3602): \[java] GuardLogStatement: False positive when compile-time constant is created from external constants - * [#4731](https://github.com/pmd/pmd/issues/4731): \[java] GuardLogStatement documentation is unclear why getters are flagged - * [#5145](https://github.com/pmd/pmd/issues/5145): \[java] False positive UnusedPrivateMethod + * [#4731](https://github.com/pmd/pmd/issues/4731): \[java] GuardLogStatement: Documentation is unclear why getters are flagged + * [#5145](https://github.com/pmd/pmd/issues/5145): \[java] UnusedPrivateMethod: False positive with method calls inside lambda * [#5151](https://github.com/pmd/pmd/issues/5151): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is a constant from another class * [#5152](https://github.com/pmd/pmd/issues/5152): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is "this" * [#5153](https://github.com/pmd/pmd/issues/5153): \[java] GuardLogStatement: Should not need to guard parameterized log messages where the replacement arg is an array element * plsql-bestpractices - * [#5132](https://github.com/pmd/pmd/issues/5132): \[plsql] TomKytesDespair - exception for more complex exception handler + * [#5132](https://github.com/pmd/pmd/issues/5132): \[plsql] TomKytesDespair: XPathException for more complex exception handler ### ๐Ÿšจ API Changes * pmd-jsp From 753dcca87721c098bc26799bda0f77b534802fb6 Mon Sep 17 00:00:00 2001 From: Andreas Dangel Date: Thu, 29 Aug 2024 09:55:33 +0200 Subject: [PATCH 58/58] [doc] Update contributors - Add @jbisotti as a contributor - Add @soloturn as a contributor - Add @schosin as a contributor --- .all-contributorsrc | 27 ++++ docs/pages/pmd/projectdocs/credits.md | 183 +++++++++++++------------- 2 files changed, 121 insertions(+), 89 deletions(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index f76cd95f82..e05387dca9 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -7708,6 +7708,33 @@ "contributions": [ "bug" ] + }, + { + "login": "jbisotti", + "name": "Jamie Bisotti", + "avatar_url": "https://avatars.githubusercontent.com/u/899712?v=4", + "profile": "https://github.com/jbisotti", + "contributions": [ + "bug" + ] + }, + { + "login": "soloturn", + "name": "soloturn", + "avatar_url": "https://avatars.githubusercontent.com/u/825568?v=4", + "profile": "https://github.com/soloturn", + "contributions": [ + "bug" + ] + }, + { + "login": "schosin", + "name": "schosin", + "avatar_url": "https://avatars.githubusercontent.com/u/1669777?v=4", + "profile": "https://github.com/schosin", + "contributions": [ + "bug" + ] } ], "contributorsPerLine": 7, diff --git a/docs/pages/pmd/projectdocs/credits.md b/docs/pages/pmd/projectdocs/credits.md index 7cbe0baec8..9bf613b252 100644 --- a/docs/pages/pmd/projectdocs/credits.md +++ b/docs/pages/pmd/projectdocs/credits.md @@ -345,751 +345,756 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d JJengility
JJengility

๐Ÿ› Jake Hemmerle
Jake Hemmerle

๐Ÿ› James Harrison
James Harrison

๐Ÿ› ๐Ÿ’ป - Jan
Jan

๐Ÿ› + Jamie Bisotti
Jamie Bisotti

๐Ÿ› + Jan
Jan

๐Ÿ› Jan Aertgeerts
Jan Aertgeerts

๐Ÿ’ป ๐Ÿ› Jan Brรผmmer
Jan Brรผmmer

๐Ÿ› Jan Tล™รญska
Jan Tล™รญska

๐Ÿ› Jan-Lukas Else
Jan-Lukas Else

๐Ÿ› Jason Qiu
Jason Qiu

๐Ÿ’ป ๐Ÿ“– Jason Williams
Jason Williams

๐Ÿ› - Jean-Paul Mayer
Jean-Paul Mayer

๐Ÿ› + Jean-Paul Mayer
Jean-Paul Mayer

๐Ÿ› Jean-Simon Larochelle
Jean-Simon Larochelle

๐Ÿ› Jeff Bartolotta
Jeff Bartolotta

๐Ÿ’ป ๐Ÿ› Jeff Hube
Jeff Hube

๐Ÿ’ป ๐Ÿ› Jeff Jensen
Jeff Jensen

๐Ÿ› Jeff May
Jeff May

๐Ÿ› Jens Gerdes
Jens Gerdes

๐Ÿ› - Jeroen Borgers
Jeroen Borgers

๐Ÿ› ๐Ÿ’ป ๐Ÿ“ข + Jeroen Borgers
Jeroen Borgers

๐Ÿ› ๐Ÿ’ป ๐Ÿ“ข Jeroen Meijer
Jeroen Meijer

๐Ÿ› Jeroen van Wilgenburg
Jeroen van Wilgenburg

๐Ÿ“– Jerome Russ
Jerome Russ

๐Ÿ› JerritEic
JerritEic

๐Ÿ’ป ๐Ÿ“– ๐Ÿ› Jiri Pejchal
Jiri Pejchal

๐Ÿ› Jithin Sunny
Jithin Sunny

๐Ÿ› - Jiล™รญ ล korpil
Jiล™รญ ล korpil

๐Ÿ› + Jiล™รญ ล korpil
Jiล™รญ ล korpil

๐Ÿ› Joao Machado
Joao Machado

๐Ÿ› Jochen Krauss
Jochen Krauss

๐Ÿ› Johan Hammar
Johan Hammar

๐Ÿ› John Karp
John Karp

๐Ÿ› John Zhang
John Zhang

๐Ÿ› John-Teng
John-Teng

๐Ÿ’ป ๐Ÿ› - Jon Moroney
Jon Moroney

๐Ÿ’ป ๐Ÿ› + Jon Moroney
Jon Moroney

๐Ÿ’ป ๐Ÿ› Jonas Geiregat
Jonas Geiregat

๐Ÿ› Jonas KeรŸler
Jonas KeรŸler

๐Ÿ› Jonathan Wiesel
Jonathan Wiesel

๐Ÿ’ป ๐Ÿ› Jordan
Jordan

๐Ÿ› Jordi Llach
Jordi Llach

๐Ÿ› Jorge Solรณrzano
Jorge Solรณrzano

๐Ÿ› - JorneVL
JorneVL

๐Ÿ› + JorneVL
JorneVL

๐Ÿ› Jose Palafox
Jose Palafox

๐Ÿ› Jose Stovall
Jose Stovall

๐Ÿ› Joseph
Joseph

๐Ÿ’ป Joseph Heenan
Joseph Heenan

๐Ÿ› Josh Feingold
Josh Feingold

๐Ÿ’ป ๐Ÿ› Josh Holthaus
Josh Holthaus

๐Ÿ› - Joshua S Arquilevich
Joshua S Arquilevich

๐Ÿ› + Joshua S Arquilevich
Joshua S Arquilevich

๐Ÿ› Joรฃo Dinis Ferreira
Joรฃo Dinis Ferreira

๐Ÿ“– Joรฃo Ferreira
Joรฃo Ferreira

๐Ÿ’ป ๐Ÿ› Joรฃo Pedro Schmitt
Joรฃo Pedro Schmitt

๐Ÿ› Juan Martรญn Sotuyo Dodero
Juan Martรญn Sotuyo Dodero

๐Ÿ’ป ๐Ÿ“– ๐Ÿ› ๐Ÿšง Juan Pablo Civile
Juan Pablo Civile

๐Ÿ› Julian Voronetsky
Julian Voronetsky

๐Ÿ› - Julien
Julien

๐Ÿ› + Julien
Julien

๐Ÿ› Julius
Julius

๐Ÿ› JustPRV
JustPRV

๐Ÿ› Justin Stroud
Justin Stroud

๐Ÿ’ป Jรถrn Huxhorn
Jรถrn Huxhorn

๐Ÿ› KThompso
KThompso

๐Ÿ› Kai Amundsen
Kai Amundsen

๐Ÿ› - Karel Vervaeke
Karel Vervaeke

๐Ÿ› + Karel Vervaeke
Karel Vervaeke

๐Ÿ› Karl-Andero Mere
Karl-Andero Mere

๐Ÿ› Karl-Philipp Richter
Karl-Philipp Richter

๐Ÿ› Karsten Silz
Karsten Silz

๐Ÿ› Kazuma Watanabe
Kazuma Watanabe

๐Ÿ› Kev
Kev

๐Ÿ› Keve Mรผller
Keve Mรผller

๐Ÿ› - Kevin Guerra
Kevin Guerra

๐Ÿ’ป + Kevin Guerra
Kevin Guerra

๐Ÿ’ป Kevin Jones
Kevin Jones

๐Ÿ› ๐Ÿ’ป Kevin Poorman
Kevin Poorman

๐Ÿ› Kevin Wayne
Kevin Wayne

๐Ÿ› Kieran Black
Kieran Black

๐Ÿ› Kirill Zubov
Kirill Zubov

๐Ÿ› Kirk Clemens
Kirk Clemens

๐Ÿ’ป ๐Ÿ› - Klaus Hartl
Klaus Hartl

๐Ÿ› + Klaus Hartl
Klaus Hartl

๐Ÿ› Koen Van Looveren
Koen Van Looveren

๐Ÿ› Kris Scheibe
Kris Scheibe

๐Ÿ’ป ๐Ÿ› Krystian Dabrowski
Krystian Dabrowski

๐Ÿ› ๐Ÿ’ป Kunal Thanki
Kunal Thanki

๐Ÿ› LaLucid
LaLucid

๐Ÿ’ป Larry Diamond
Larry Diamond

๐Ÿ’ป ๐Ÿ› - Lars Knickrehm
Lars Knickrehm

๐Ÿ› + Lars Knickrehm
Lars Knickrehm

๐Ÿ› Laurent Bovet
Laurent Bovet

๐Ÿ› ๐Ÿ’ป Leo Gutierrez
Leo Gutierrez

๐Ÿ› LiGaOg
LiGaOg

๐Ÿ’ป Liam Sharp
Liam Sharp

๐Ÿ› Lintsi
Lintsi

๐Ÿ› Linus Fernandes
Linus Fernandes

๐Ÿ› - Lixon Lookose
Lixon Lookose

๐Ÿ› + Lixon Lookose
Lixon Lookose

๐Ÿ› Logesh
Logesh

๐Ÿ› Lorenzo Gabriele
Lorenzo Gabriele

๐Ÿ› Loรฏc Ledoyen
Loรฏc Ledoyen

๐Ÿ› Lucas
Lucas

๐Ÿ› Lucas Silva
Lucas Silva

๐Ÿ› Lucas Soncini
Lucas Soncini

๐Ÿ’ป ๐Ÿ› - Luis Alcantar
Luis Alcantar

๐Ÿ’ป + Luis Alcantar
Luis Alcantar

๐Ÿ’ป Lukasz Slonina
Lukasz Slonina

๐Ÿ› Lukebray
Lukebray

๐Ÿ› Lynn
Lynn

๐Ÿ’ป ๐Ÿ› Lyor Goldstein
Lyor Goldstein

๐Ÿ› MCMicS
MCMicS

๐Ÿ› Macarse
Macarse

๐Ÿ› - Machine account for PMD
Machine account for PMD

๐Ÿ’ป + Machine account for PMD
Machine account for PMD

๐Ÿ’ป Maciek Siemczyk
Maciek Siemczyk

๐Ÿ› Maikel Steneker
Maikel Steneker

๐Ÿ’ป ๐Ÿ› Maksim Moiseikin
Maksim Moiseikin

๐Ÿ› Manfred Koch
Manfred Koch

๐Ÿ› Manuel Moya Ferrer
Manuel Moya Ferrer

๐Ÿ’ป ๐Ÿ› Manuel Ryan
Manuel Ryan

๐Ÿ› - Marat Vyshegorodtsev
Marat Vyshegorodtsev

๐Ÿ› + Marat Vyshegorodtsev
Marat Vyshegorodtsev

๐Ÿ› Marcel Hรคrle
Marcel Hรคrle

๐Ÿ› Marcello Fialho
Marcello Fialho

๐Ÿ› Marcin Dฤ…browski
Marcin Dฤ…browski

๐Ÿ’ป Marcin Rataj
Marcin Rataj

๐Ÿ› Marcono1234
Marcono1234

๐Ÿ› Mark Adamcin
Mark Adamcin

๐Ÿ› - Mark Hall
Mark Hall

๐Ÿ’ป ๐Ÿ› + Mark Hall
Mark Hall

๐Ÿ’ป ๐Ÿ› Mark Kolich
Mark Kolich

๐Ÿ› Mark Pritchard
Mark Pritchard

๐Ÿ› Markus Rathgeb
Markus Rathgeb

๐Ÿ› Marquis Wang
Marquis Wang

๐Ÿ› MartGit
MartGit

๐Ÿ› Martin Feldsztejn
Martin Feldsztejn

๐Ÿ› - Martin Lehmann
Martin Lehmann

๐Ÿ› + Martin Lehmann
Martin Lehmann

๐Ÿ› Martin Spamer
Martin Spamer

๐Ÿ› Martin Tarjรกnyi
Martin Tarjรกnyi

๐Ÿ› MatFl
MatFl

๐Ÿ› Mateusz Stefanski
Mateusz Stefanski

๐Ÿ› Mathieu Gouin
Mathieu Gouin

๐Ÿ› MatiasComercio
MatiasComercio

๐Ÿ’ป ๐Ÿ› - Matt Benson
Matt Benson

๐Ÿ› + Matt Benson
Matt Benson

๐Ÿ› Matt De Poorter
Matt De Poorter

๐Ÿ› Matt Hargett
Matt Hargett

๐Ÿ’ป ๐Ÿ’ต Matt Harrah
Matt Harrah

๐Ÿ› Matt Nelson
Matt Nelson

๐Ÿ› Matthew Amos
Matthew Amos

๐Ÿ› Matthew Duggan
Matthew Duggan

๐Ÿ› - Matthew Hall
Matthew Hall

๐Ÿ› + Matthew Hall
Matthew Hall

๐Ÿ› Matรญas Fraga
Matรญas Fraga

๐Ÿ’ป ๐Ÿ› Maxime Robert
Maxime Robert

๐Ÿ’ป ๐Ÿ› MetaBF
MetaBF

๐Ÿ› Metin Dagcilar
Metin Dagcilar

๐Ÿ› Michael
Michael

๐Ÿ› Michael Bell
Michael Bell

๐Ÿ› - Michael Bernstein
Michael Bernstein

๐Ÿ› + Michael Bernstein
Michael Bernstein

๐Ÿ› Michael Clay
Michael Clay

๐Ÿ› Michael Dombrowski
Michael Dombrowski

๐Ÿ› Michael Hausegger
Michael Hausegger

๐Ÿ› Michael Hoefer
Michael Hoefer

๐Ÿ› Michael Kolesnikov
Michael Kolesnikov

๐Ÿ› Michael Mรถbius
Michael Mรถbius

๐Ÿ› - Michael N. Lipp
Michael N. Lipp

๐Ÿ› + Michael N. Lipp
Michael N. Lipp

๐Ÿ› Michael Pellegrini
Michael Pellegrini

๐Ÿ› Michal Kordas
Michal Kordas

๐Ÿ› Michaล‚ Borek
Michaล‚ Borek

๐Ÿ› Michaล‚ Kuliล„ski
Michaล‚ Kuliล„ski

๐Ÿ› Miguel Nรบรฑez Dรญaz-Montes
Miguel Nรบรฑez Dรญaz-Montes

๐Ÿ› Mihai Ionut
Mihai Ionut

๐Ÿ› - Mikhail Kuchma
Mikhail Kuchma

๐Ÿ› + Mikhail Kuchma
Mikhail Kuchma

๐Ÿ› Mirek Hankus
Mirek Hankus

๐Ÿ› Mitch Spano
Mitch Spano

๐Ÿ› Mladjan Gadzic
Mladjan Gadzic

๐Ÿ› MrAngry52
MrAngry52

๐Ÿ› Muminur Choudhury
Muminur Choudhury

๐Ÿ› Mykhailo Palahuta
Mykhailo Palahuta

๐Ÿ’ป ๐Ÿ› - Nagendra Kumar Singh
Nagendra Kumar Singh

๐Ÿ› + Nagendra Kumar Singh
Nagendra Kumar Singh

๐Ÿ› Nahuel Barrios
Nahuel Barrios

๐Ÿ› Nakul Sharma
Nakul Sharma

๐Ÿ› Nathan Braun
Nathan Braun

๐Ÿ› Nathan Reynolds
Nathan Reynolds

๐Ÿ› Nathan Reynolds
Nathan Reynolds

๐Ÿ› Nathanaรซl
Nathanaรซl

๐Ÿ› - Naveen
Naveen

๐Ÿ’ป + Naveen
Naveen

๐Ÿ’ป Nazdravi
Nazdravi

๐Ÿ› Neha-Dhonde
Neha-Dhonde

๐Ÿ› Nicholas Doyle
Nicholas Doyle

๐Ÿ› Nick Butcher
Nick Butcher

๐Ÿ› Nico Gallinal
Nico Gallinal

๐Ÿ› Nicola Dal Maso
Nicola Dal Maso

๐Ÿ› - Nicolas Filotto
Nicolas Filotto

๐Ÿ’ป + Nicolas Filotto
Nicolas Filotto

๐Ÿ’ป Nicolas Vervelle
Nicolas Vervelle

๐Ÿ› Nicolas Vuillamy
Nicolas Vuillamy

๐Ÿ“– Nikita Chursin
Nikita Chursin

๐Ÿ› Niklas Baudy
Niklas Baudy

๐Ÿ› Nikolas Havrikov
Nikolas Havrikov

๐Ÿ› Nilesh Virkar
Nilesh Virkar

๐Ÿ› - Nimit Patel
Nimit Patel

๐Ÿ› + Nimit Patel
Nimit Patel

๐Ÿ› Niranjan Harpale
Niranjan Harpale

๐Ÿ› Nirvik Patel
Nirvik Patel

๐Ÿ’ป Noah Sussman
Noah Sussman

๐Ÿ› Noah0120
Noah0120

๐Ÿ› Noam Tamim
Noam Tamim

๐Ÿ› Noel Grandin
Noel Grandin

๐Ÿ› - Olaf Haalstra
Olaf Haalstra

๐Ÿ› + Olaf Haalstra
Olaf Haalstra

๐Ÿ› Oleg Andreych
Oleg Andreych

๐Ÿ’ป ๐Ÿ› Oleg Pavlenko
Oleg Pavlenko

๐Ÿ› Oleksii Dykov
Oleksii Dykov

๐Ÿ’ป ๐Ÿ› Oliver Eikemeier
Oliver Eikemeier

๐Ÿ› Oliver Siegmar
Oliver Siegmar

๐Ÿ’ต Olivier Parent
Olivier Parent

๐Ÿ’ป ๐Ÿ› - Ollie Abbey
Ollie Abbey

๐Ÿ’ป ๐Ÿ› + Ollie Abbey
Ollie Abbey

๐Ÿ’ป ๐Ÿ› OverDrone
OverDrone

๐Ÿ› Ozan Gulle
Ozan Gulle

๐Ÿ’ป ๐Ÿ› PUNEET JAIN
PUNEET JAIN

๐Ÿ› Parbati Bose
Parbati Bose

๐Ÿ› Paul Berg
Paul Berg

๐Ÿ› Paul Guyot
Paul Guyot

๐Ÿ’ป - Pavel Bludov
Pavel Bludov

๐Ÿ› + Pavel Bludov
Pavel Bludov

๐Ÿ› Pavel Miฤka
Pavel Miฤka

๐Ÿ› Pedro Nuno Santos
Pedro Nuno Santos

๐Ÿ› Pedro Rijo
Pedro Rijo

๐Ÿ› Pelisse Romain
Pelisse Romain

๐Ÿ’ป ๐Ÿ“– ๐Ÿ› Per Abich
Per Abich

๐Ÿ’ป Pete Davids
Pete Davids

๐Ÿ› - Peter Bruin
Peter Bruin

๐Ÿ› + Peter Bruin
Peter Bruin

๐Ÿ› Peter Chittum
Peter Chittum

๐Ÿ’ป ๐Ÿ› Peter Cudmore
Peter Cudmore

๐Ÿ› Peter Kasson
Peter Kasson

๐Ÿ› Peter Kofler
Peter Kofler

๐Ÿ› Peter Paul Bakker
Peter Paul Bakker

๐Ÿ’ป Peter Rader
Peter Rader

๐Ÿ› - Pham Hai Trung
Pham Hai Trung

๐Ÿ› + Pham Hai Trung
Pham Hai Trung

๐Ÿ› Philip Graf
Philip Graf

๐Ÿ’ป ๐Ÿ› Philip Hachey
Philip Hachey

๐Ÿ› Philippe Ozil
Philippe Ozil

๐Ÿ› Phinehas Artemix
Phinehas Artemix

๐Ÿ› Phokham Nonava
Phokham Nonava

๐Ÿ› Pim van der Loos
Pim van der Loos

๐Ÿ’ป โš ๏ธ - Piotr Szymaล„ski
Piotr Szymaล„ski

๐Ÿ› + Piotr Szymaล„ski
Piotr Szymaล„ski

๐Ÿ› Piotrek ลปygieล‚o
Piotrek ลปygieล‚o

๐Ÿ’ป ๐Ÿ› ๐Ÿ“– Pranay Jaiswal
Pranay Jaiswal

๐Ÿ› Prasad Kamath
Prasad Kamath

๐Ÿ› Prasanna
Prasanna

๐Ÿ› Presh-AR
Presh-AR

๐Ÿ› Puneet1726
Puneet1726

๐Ÿ› - RBRi
RBRi

๐Ÿ› + RBRi
RBRi

๐Ÿ› Rafael Cortรชs
Rafael Cortรชs

๐Ÿ› RaheemShaik999
RaheemShaik999

๐Ÿ› RajeshR
RajeshR

๐Ÿ’ป ๐Ÿ› Ramachandra Mohan
Ramachandra Mohan

๐Ÿ› Ramel0921
Ramel0921

๐Ÿ› Raquel Pau
Raquel Pau

๐Ÿ› - Ravikiran Janardhana
Ravikiran Janardhana

๐Ÿ› + Ravikiran Janardhana
Ravikiran Janardhana

๐Ÿ› Reda Benhemmouche
Reda Benhemmouche

๐Ÿ› Reinhard Schiedermeier
Reinhard Schiedermeier

๐Ÿ› Renato Oliveira
Renato Oliveira

๐Ÿ’ป ๐Ÿ› Rich DiCroce
Rich DiCroce

๐Ÿ› Richard Corfield
Richard Corfield

๐Ÿ’ป Richard Corfield
Richard Corfield

๐Ÿ› ๐Ÿ’ป - Riot R1cket
Riot R1cket

๐Ÿ› + Riot R1cket
Riot R1cket

๐Ÿ› Rishabh Jain
Rishabh Jain

๐Ÿ› RishabhDeep Singh
RishabhDeep Singh

๐Ÿ› Rob Baillie
Rob Baillie

๐Ÿ› Robbie Martinus
Robbie Martinus

๐Ÿ’ป ๐Ÿ› Robert Henry
Robert Henry

๐Ÿ› Robert Mihaly
Robert Mihaly

๐Ÿ› - Robert Painsi
Robert Painsi

๐Ÿ› + Robert Painsi
Robert Painsi

๐Ÿ› Robert Russell
Robert Russell

๐Ÿ› Robert Sรถsemann
Robert Sรถsemann

๐Ÿ’ป ๐Ÿ“– ๐Ÿ“ข ๐Ÿ› Robert Whitebit
Robert Whitebit

๐Ÿ› Robin Richtsfeld
Robin Richtsfeld

๐Ÿ› Robin Stocker
Robin Stocker

๐Ÿ’ป ๐Ÿ› Robin Wils
Robin Wils

๐Ÿ› - RochusOest
RochusOest

๐Ÿ› + RochusOest
RochusOest

๐Ÿ› Rodolfo Noviski
Rodolfo Noviski

๐Ÿ› Rodrigo Casara
Rodrigo Casara

๐Ÿ› Rodrigo Fernandes
Rodrigo Fernandes

๐Ÿ› Roman Salvador
Roman Salvador

๐Ÿ’ป ๐Ÿ› Ronald Blaschke
Ronald Blaschke

๐Ÿ› Rรณbert Papp
Rรณbert Papp

๐Ÿ› - Saikat Sengupta
Saikat Sengupta

๐Ÿ› + Saikat Sengupta
Saikat Sengupta

๐Ÿ› Saksham Handu
Saksham Handu

๐Ÿ› Saladoc
Saladoc

๐Ÿ› Salesforce Bob Lightning
Salesforce Bob Lightning

๐Ÿ› Sam Carlberg
Sam Carlberg

๐Ÿ› Sashko
Sashko

๐Ÿ’ป Satoshi Kubo
Satoshi Kubo

๐Ÿ› - Scott Kennedy
Scott Kennedy

๐Ÿ› + Scott Kennedy
Scott Kennedy

๐Ÿ› Scott Wells
Scott Wells

๐Ÿ› ๐Ÿ’ป Scrates1
Scrates1

๐Ÿ› ๐Ÿ’ป Scrsloota
Scrsloota

๐Ÿ’ป Sebastian Bรถgl
Sebastian Bรถgl

๐Ÿ› Sebastian Davids
Sebastian Davids

๐Ÿ› Sebastian Schuberth
Sebastian Schuberth

๐Ÿ› - Sebastian Schwarz
Sebastian Schwarz

๐Ÿ› + Sebastian Schwarz
Sebastian Schwarz

๐Ÿ› Seren
Seren

๐Ÿ› ๐Ÿ’ป Sergey Gorbaty
Sergey Gorbaty

๐Ÿ› Sergey Kozlov
Sergey Kozlov

๐Ÿ› Sergey Yanzin
Sergey Yanzin

๐Ÿ’ป ๐Ÿ› Seth Wilcox
Seth Wilcox

๐Ÿ’ป Shai Bennathan
Shai Bennathan

๐Ÿ› ๐Ÿ’ป - Shubham
Shubham

๐Ÿ’ป ๐Ÿ› + Shubham
Shubham

๐Ÿ’ป ๐Ÿ› Simon Abykov
Simon Abykov

๐Ÿ’ป ๐Ÿ› Simon Xiao
Simon Xiao

๐Ÿ› Srinivasan Venkatachalam
Srinivasan Venkatachalam

๐Ÿ› Stanislav Gromov
Stanislav Gromov

๐Ÿ› Stanislav Myachenkov
Stanislav Myachenkov

๐Ÿ’ป Stefan Birkner
Stefan Birkner

๐Ÿ› - Stefan Bohn
Stefan Bohn

๐Ÿ› + Stefan Bohn
Stefan Bohn

๐Ÿ› Stefan Endrullis
Stefan Endrullis

๐Ÿ› Stefan Klรถss-Schuster
Stefan Klรถss-Schuster

๐Ÿ› Stefan Wolf
Stefan Wolf

๐Ÿ› Stephan H. Wissel
Stephan H. Wissel

๐Ÿ› Stephen
Stephen

๐Ÿ› Stephen Carter
Stephen Carter

๐Ÿ› - Stephen Friedrich
Stephen Friedrich

๐Ÿ› + Stephen Friedrich
Stephen Friedrich

๐Ÿ› Steve Babula
Steve Babula

๐Ÿ’ป Steven Stearns
Steven Stearns

๐Ÿ› ๐Ÿ’ป Stexxe
Stexxe

๐Ÿ› Stian Lรฅgstad
Stian Lรฅgstad

๐Ÿ› StuartClayton5
StuartClayton5

๐Ÿ› Supun Arunoda
Supun Arunoda

๐Ÿ› - Suren Abrahamyan
Suren Abrahamyan

๐Ÿ› + Suren Abrahamyan
Suren Abrahamyan

๐Ÿ› Suvashri
Suvashri

๐Ÿ“– SwatiBGupta1110
SwatiBGupta1110

๐Ÿ› SyedThoufich
SyedThoufich

๐Ÿ› Szymon Sasin
Szymon Sasin

๐Ÿ› T-chuangxin
T-chuangxin

๐Ÿ› TERAI Atsuhiro
TERAI Atsuhiro

๐Ÿ› - TIOBE Software
TIOBE Software

๐Ÿ’ป ๐Ÿ› + TIOBE Software
TIOBE Software

๐Ÿ’ป ๐Ÿ› Tarush Singh
Tarush Singh

๐Ÿ’ป Taylor Smock
Taylor Smock

๐Ÿ› Techeira Damiรกn
Techeira Damiรกn

๐Ÿ’ป ๐Ÿ› Ted Husted
Ted Husted

๐Ÿ› TehBakker
TehBakker

๐Ÿ› The Gitter Badger
The Gitter Badger

๐Ÿ› - Theodoor
Theodoor

๐Ÿ› + Theodoor
Theodoor

๐Ÿ› Thiago Henrique Hรผpner
Thiago Henrique Hรผpner

๐Ÿ› Thibault Meyer
Thibault Meyer

๐Ÿ› Thomas Gรผttler
Thomas Gรผttler

๐Ÿ› Thomas Jones-Low
Thomas Jones-Low

๐Ÿ› Thomas Smith
Thomas Smith

๐Ÿ’ป ๐Ÿ› ThrawnCA
ThrawnCA

๐Ÿ› - Thu Vo
Thu Vo

๐Ÿ› + Thu Vo
Thu Vo

๐Ÿ› Thunderforge
Thunderforge

๐Ÿ’ป ๐Ÿ› Tim van der Lippe
Tim van der Lippe

๐Ÿ› Tobias Weimer
Tobias Weimer

๐Ÿ’ป ๐Ÿ› Tom Copeland
Tom Copeland

๐Ÿ› ๐Ÿ’ป ๐Ÿ“– Tom Daly
Tom Daly

๐Ÿ› Tomas
Tomas

๐Ÿ› - Tomer Figenblat
Tomer Figenblat

๐Ÿ› + Tomer Figenblat
Tomer Figenblat

๐Ÿ› Tomi De Lucca
Tomi De Lucca

๐Ÿ’ป ๐Ÿ› Torsten Kleiber
Torsten Kleiber

๐Ÿ› TrackerSB
TrackerSB

๐Ÿ› Tyson Stewart
Tyson Stewart

๐Ÿ› Ullrich Hafner
Ullrich Hafner

๐Ÿ› Utku Cuhadaroglu
Utku Cuhadaroglu

๐Ÿ’ป ๐Ÿ› - Valentin Brandl
Valentin Brandl

๐Ÿ› + Valentin Brandl
Valentin Brandl

๐Ÿ› Valeria
Valeria

๐Ÿ› Valery Yatsynovich
Valery Yatsynovich

๐Ÿ“– Vasily Anisimov
Vasily Anisimov

๐Ÿ› Vibhor Goyal
Vibhor Goyal

๐Ÿ› Vickenty Fesunov
Vickenty Fesunov

๐Ÿ› Victor Noรซl
Victor Noรซl

๐Ÿ› - Vincent Galloy
Vincent Galloy

๐Ÿ’ป + Vincent Galloy
Vincent Galloy

๐Ÿ’ป Vincent HUYNH
Vincent HUYNH

๐Ÿ› Vincent Maurin
Vincent Maurin

๐Ÿ› Vincent Privat
Vincent Privat

๐Ÿ› Vishhwas
Vishhwas

๐Ÿ› Vishv_Android
Vishv_Android

๐Ÿ› Vitaly
Vitaly

๐Ÿ› - Vitaly Polonetsky
Vitaly Polonetsky

๐Ÿ› + Vitaly Polonetsky
Vitaly Polonetsky

๐Ÿ› Vojtech Polivka
Vojtech Polivka

๐Ÿ› Vsevolod Zholobov
Vsevolod Zholobov

๐Ÿ› Vyom Yadav
Vyom Yadav

๐Ÿ’ป Wang Shidong
Wang Shidong

๐Ÿ› Waqas Ahmed
Waqas Ahmed

๐Ÿ› Wayne J. Earl
Wayne J. Earl

๐Ÿ› - Wchenghui
Wchenghui

๐Ÿ› + Wchenghui
Wchenghui

๐Ÿ› Wener
Wener

๐Ÿ’ป Will Winder
Will Winder

๐Ÿ› William Brockhus
William Brockhus

๐Ÿ’ป ๐Ÿ› Wilson Kurniawan
Wilson Kurniawan

๐Ÿ› Wim Deblauwe
Wim Deblauwe

๐Ÿ› Woongsik Choi
Woongsik Choi

๐Ÿ› - XenoAmess
XenoAmess

๐Ÿ’ป ๐Ÿ› + XenoAmess
XenoAmess

๐Ÿ’ป ๐Ÿ› Yang
Yang

๐Ÿ’ป YaroslavTER
YaroslavTER

๐Ÿ› Yasar Shaikh
Yasar Shaikh

๐Ÿ’ป Young Chan
Young Chan

๐Ÿ’ป ๐Ÿ› YuJin Kim
YuJin Kim

๐Ÿ› Yuri Dolzhenko
Yuri Dolzhenko

๐Ÿ› - Yurii Dubinka
Yurii Dubinka

๐Ÿ› + Yurii Dubinka
Yurii Dubinka

๐Ÿ› Zoltan Farkas
Zoltan Farkas

๐Ÿ› Zustin
Zustin

๐Ÿ› aaronhurst-google
aaronhurst-google

๐Ÿ› ๐Ÿ’ป alexmodis
alexmodis

๐Ÿ› andreoss
andreoss

๐Ÿ› andrey81inmd
andrey81inmd

๐Ÿ’ป ๐Ÿ› - anicoara
anicoara

๐Ÿ› + anicoara
anicoara

๐Ÿ› arunprasathav
arunprasathav

๐Ÿ› asiercamara
asiercamara

๐Ÿ› astillich-igniti
astillich-igniti

๐Ÿ’ป avesolovksyy
avesolovksyy

๐Ÿ› avishvat
avishvat

๐Ÿ› avivmu
avivmu

๐Ÿ› - axelbarfod1
axelbarfod1

๐Ÿ› + axelbarfod1
axelbarfod1

๐Ÿ› b-3-n
b-3-n

๐Ÿ› balbhadra9
balbhadra9

๐Ÿ› base23de
base23de

๐Ÿ› bergander
bergander

๐Ÿ› ๐Ÿ’ป berkam
berkam

๐Ÿ’ป ๐Ÿ› breizh31
breizh31

๐Ÿ› - caesarkim
caesarkim

๐Ÿ› + caesarkim
caesarkim

๐Ÿ› carolyujing
carolyujing

๐Ÿ› cbfiddle
cbfiddle

๐Ÿ› cesares-basilico
cesares-basilico

๐Ÿ› chrite
chrite

๐Ÿ› ciufudean
ciufudean

๐Ÿ“– cobratbq
cobratbq

๐Ÿ› - coladict
coladict

๐Ÿ› + coladict
coladict

๐Ÿ› cosmoJFH
cosmoJFH

๐Ÿ› cristalp
cristalp

๐Ÿ› crunsk
crunsk

๐Ÿ› cwholmes
cwholmes

๐Ÿ› cyberjj999
cyberjj999

๐Ÿ› cyw3
cyw3

๐Ÿ› ๐Ÿ“– - d1ss0nanz
d1ss0nanz

๐Ÿ› + d1ss0nanz
d1ss0nanz

๐Ÿ› dague1
dague1

๐Ÿ“– dalizi007
dalizi007

๐Ÿ’ป danbrycefairsailcom
danbrycefairsailcom

๐Ÿ› dariansanity
dariansanity

๐Ÿ› darrenmiliband
darrenmiliband

๐Ÿ› davidburstrom
davidburstrom

๐Ÿ› - dbirkman-paloalto
dbirkman-paloalto

๐Ÿ› + dbirkman-paloalto
dbirkman-paloalto

๐Ÿ› deepak-patra
deepak-patra

๐Ÿ› dependabot[bot]
dependabot[bot]

๐Ÿ’ป ๐Ÿ› dinesh150
dinesh150

๐Ÿ› diziaq
diziaq

๐Ÿ› dreaminpast123
dreaminpast123

๐Ÿ› duanyanan
duanyanan

๐Ÿ› - dutt-sanjay
dutt-sanjay

๐Ÿ› + dutt-sanjay
dutt-sanjay

๐Ÿ› duursma
duursma

๐Ÿ’ป dylanleung
dylanleung

๐Ÿ› dzeigler
dzeigler

๐Ÿ› eant60
eant60

๐Ÿ› ekkirala
ekkirala

๐Ÿ› emersonmoura
emersonmoura

๐Ÿ› - emouty
emouty

๐Ÿ’ป + emouty
emouty

๐Ÿ’ป eugenepugach
eugenepugach

๐Ÿ› fairy
fairy

๐Ÿ› filiprafalowicz
filiprafalowicz

๐Ÿ’ป flxbl-io
flxbl-io

๐Ÿ’ต foxmason
foxmason

๐Ÿ› frankegabor
frankegabor

๐Ÿ› - frankl
frankl

๐Ÿ› + frankl
frankl

๐Ÿ› freafrea
freafrea

๐Ÿ› fsapatin
fsapatin

๐Ÿ› gearsethenry
gearsethenry

๐Ÿ› gracia19
gracia19

๐Ÿ› guo fei
guo fei

๐Ÿ› gurmsc5
gurmsc5

๐Ÿ› - gwilymatgearset
gwilymatgearset

๐Ÿ’ป ๐Ÿ› + gwilymatgearset
gwilymatgearset

๐Ÿ’ป ๐Ÿ› haigsn
haigsn

๐Ÿ› hemanshu070
hemanshu070

๐Ÿ› henrik242
henrik242

๐Ÿ› hongpuwu
hongpuwu

๐Ÿ› hvbtup
hvbtup

๐Ÿ’ป ๐Ÿ› igniti GmbH
igniti GmbH

๐Ÿ› - ilovezfs
ilovezfs

๐Ÿ› + ilovezfs
ilovezfs

๐Ÿ› itaigilo
itaigilo

๐Ÿ› jakivey32
jakivey32

๐Ÿ› jbennett2091
jbennett2091

๐Ÿ› jcamerin
jcamerin

๐Ÿ› jkeener1
jkeener1

๐Ÿ› jmetertea
jmetertea

๐Ÿ› - johnra2
johnra2

๐Ÿ’ป + johnra2
johnra2

๐Ÿ’ป johnzhao9
johnzhao9

๐Ÿ› josemanuelrolon
josemanuelrolon

๐Ÿ’ป ๐Ÿ› kabroxiko
kabroxiko

๐Ÿ’ป ๐Ÿ› karthikaiyasamy
karthikaiyasamy

๐Ÿ“– karwer
karwer

๐Ÿ› kaulonline
kaulonline

๐Ÿ› - kdaemonv
kdaemonv

๐Ÿ› + kdaemonv
kdaemonv

๐Ÿ› kdebski85
kdebski85

๐Ÿ› ๐Ÿ’ป kenji21
kenji21

๐Ÿ’ป ๐Ÿ› kfranic
kfranic

๐Ÿ› khalidkh
khalidkh

๐Ÿ› koalalam
koalalam

๐Ÿ› krzyk
krzyk

๐Ÿ› - lasselindqvist
lasselindqvist

๐Ÿ› + lasselindqvist
lasselindqvist

๐Ÿ› lgemeinhardt
lgemeinhardt

๐Ÿ› lihuaib
lihuaib

๐Ÿ› liqingjun123
liqingjun123

๐Ÿ› lonelyma1021
lonelyma1021

๐Ÿ› lpeddy
lpeddy

๐Ÿ› lujiefsi
lujiefsi

๐Ÿ’ป - lukelukes
lukelukes

๐Ÿ’ป + lukelukes
lukelukes

๐Ÿ’ป lyriccoder
lyriccoder

๐Ÿ› marcelmore
marcelmore

๐Ÿ› matchbox
matchbox

๐Ÿ› matthiaskraaz
matthiaskraaz

๐Ÿ› meandonlyme
meandonlyme

๐Ÿ› mikesive
mikesive

๐Ÿ› - milossesic
milossesic

๐Ÿ› + milossesic
milossesic

๐Ÿ› mluckam
mluckam

๐Ÿ’ป ๐Ÿ› mohan-chinnappan-n
mohan-chinnappan-n

๐Ÿ’ป mriddell95
mriddell95

๐Ÿ› mrlzh
mrlzh

๐Ÿ› msloan
msloan

๐Ÿ› mucharlaravalika
mucharlaravalika

๐Ÿ› - mvenneman
mvenneman

๐Ÿ› + mvenneman
mvenneman

๐Ÿ› nareshl119
nareshl119

๐Ÿ› nicolas-harraudeau-sonarsource
nicolas-harraudeau-sonarsource

๐Ÿ› noerremark
noerremark

๐Ÿ› novsirion
novsirion

๐Ÿ› nwcm
nwcm

๐Ÿ“– ๐Ÿ› ๐Ÿ’ป oggboy
oggboy

๐Ÿ› - oinume
oinume

๐Ÿ› + oinume
oinume

๐Ÿ› orimarko
orimarko

๐Ÿ’ป ๐Ÿ› pablogomez2197
pablogomez2197

๐Ÿ› pacvz
pacvz

๐Ÿ’ป pallavi agarwal
pallavi agarwal

๐Ÿ› parksungrin
parksungrin

๐Ÿ› patpatpat123
patpatpat123

๐Ÿ› - patriksevallius
patriksevallius

๐Ÿ› + patriksevallius
patriksevallius

๐Ÿ› pbrajesh1
pbrajesh1

๐Ÿ› phoenix384
phoenix384

๐Ÿ› piotrszymanski-sc
piotrszymanski-sc

๐Ÿ’ป plan3d
plan3d

๐Ÿ› poojasix
poojasix

๐Ÿ› prabhushrikant
prabhushrikant

๐Ÿ› - pujitha8783
pujitha8783

๐Ÿ› + pujitha8783
pujitha8783

๐Ÿ› r-r-a-j
r-r-a-j

๐Ÿ› raghujayjunk
raghujayjunk

๐Ÿ› rajeshveera
rajeshveera

๐Ÿ› rajeswarreddy88
rajeswarreddy88

๐Ÿ› recdevs
recdevs

๐Ÿ› reudismam
reudismam

๐Ÿ’ป ๐Ÿ› - rijkt
rijkt

๐Ÿ› + rijkt
rijkt

๐Ÿ› rillig-tk
rillig-tk

๐Ÿ› rmohan20
rmohan20

๐Ÿ’ป ๐Ÿ› rnveach
rnveach

๐Ÿ› rxmicro
rxmicro

๐Ÿ› ryan-gustafson
ryan-gustafson

๐Ÿ’ป ๐Ÿ› sabi0
sabi0

๐Ÿ› - scais
scais

๐Ÿ› + scais
scais

๐Ÿ› + schosin
schosin

๐Ÿ› screamingfrog
screamingfrog

๐Ÿ’ต sebbASF
sebbASF

๐Ÿ› sergeygorbaty
sergeygorbaty

๐Ÿ’ป shilko2013
shilko2013

๐Ÿ› shiomiyan
shiomiyan

๐Ÿ“– - simeonKondr
simeonKondr

๐Ÿ› - snajberk
snajberk

๐Ÿ› + simeonKondr
simeonKondr

๐Ÿ› + snajberk
snajberk

๐Ÿ› sniperrifle2004
sniperrifle2004

๐Ÿ› snuyanzin
snuyanzin

๐Ÿ› ๐Ÿ’ป + soloturn
soloturn

๐Ÿ› soyodream
soyodream

๐Ÿ› sratz
sratz

๐Ÿ› + + stonio
stonio

๐Ÿ› sturton
sturton

๐Ÿ’ป ๐Ÿ› sudharmohan
sudharmohan

๐Ÿ› - - suruchidawar
suruchidawar

๐Ÿ› svenfinitiv
svenfinitiv

๐Ÿ› szymanp23
szymanp23

๐Ÿ› ๐Ÿ’ป tashiscool
tashiscool

๐Ÿ› + + test-git-hook
test-git-hook

๐Ÿ› testation21
testation21

๐Ÿ’ป ๐Ÿ› thanosa
thanosa

๐Ÿ› - - tiandiyixian
tiandiyixian

๐Ÿ› tobwoerk
tobwoerk

๐Ÿ› tprouvot
tprouvot

๐Ÿ› ๐Ÿ’ป trentchilders
trentchilders

๐Ÿ› + + triandicAnt
triandicAnt

๐Ÿ› trishul14
trishul14

๐Ÿ› tsui
tsui

๐Ÿ› - - wangzitom12306
wangzitom12306

๐Ÿ› winhkey
winhkey

๐Ÿ› witherspore
witherspore

๐Ÿ› wjljack
wjljack

๐Ÿ› + + wuchiuwong
wuchiuwong

๐Ÿ› xingsong
xingsong

๐Ÿ› xioayuge
xioayuge

๐Ÿ› - - xnYi9wRezm
xnYi9wRezm

๐Ÿ’ป ๐Ÿ› xuanuy
xuanuy

๐Ÿ› xyf0921
xyf0921

๐Ÿ› yalechen-cyw3
yalechen-cyw3

๐Ÿ› + + yasuharu-sato
yasuharu-sato

๐Ÿ› zenglian
zenglian

๐Ÿ› zgrzyt93
zgrzyt93

๐Ÿ’ป ๐Ÿ› - - zh3ng
zh3ng

๐Ÿ› zt_soft
zt_soft

๐Ÿ› ztt79
ztt79

๐Ÿ› zzzzfeng
zzzzfeng

๐Ÿ› + + รrpรกd Magosรกnyi
รrpรกd Magosรกnyi

๐Ÿ› ไปป่ดตๆฐ
ไปป่ดตๆฐ

๐Ÿ› ่Œ…ๅปถๅฎ‰
่Œ…ๅปถๅฎ‰

๐Ÿ’ป