diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index a6b2776a3f..aea74390ba 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -22,6 +22,8 @@ This is a {{ site.pmd.release_type }} release. * [#4201](https://github.com/pmd/pmd/issues/4201): \[java] CommentDefaultAccessModifier should consider lombok's @Value * java-design * [#4200](https://github.com/pmd/pmd/issues/4200): \[java] ClassWithOnlyPrivateConstructorsShouldBeFinal should consider lombok's @Value +* java-errorprone + * [#4172](https://github.com/pmd/pmd/issues/4172): \[java] InvalidLogMessageFormat false positive on externally formatted strings ### API Changes diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/InvalidLogMessageFormatRule.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/InvalidLogMessageFormatRule.java index 2322134244..6f227c94e5 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/InvalidLogMessageFormatRule.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule/errorprone/InvalidLogMessageFormatRule.java @@ -262,14 +262,41 @@ public class InvalidLogMessageFormatRule extends AbstractJavaRule { + params.size(); } - private boolean isStringFormatCall(ASTExpression node) { - if (node.getNumChildren() > 0 && node.getChild(0) instanceof ASTPrimaryExpression - && node.getChild(0).getNumChildren() > 0 && node.getChild(0).getChild(0) instanceof ASTPrimaryPrefix - && node.getChild(0).getChild(0).getNumChildren() > 0 && node.getChild(0).getChild(0).getChild(0) instanceof ASTName) { - String name = node.getChild(0).getChild(0).getChild(0).getImage(); + /** + * Checks for {@code String.format("%s", x)} and {@code "%s".formatted(x)} calls. + */ + private boolean isStringFormatCall(ASTExpression expression) { + ASTPrimaryExpression primaryExpression = null; + ASTPrimaryPrefix primaryPrefix = null; + + // Check beginning of tree: Expression/PrimaryExpression + if (expression.getNumChildren() > 0 && expression.getChild(0) instanceof ASTPrimaryExpression) { + primaryExpression = (ASTPrimaryExpression) expression.getChild(0); + } + + if (primaryExpression != null + && primaryExpression.getNumChildren() > 0 && primaryExpression.getChild(0) instanceof ASTPrimaryPrefix) { + primaryPrefix = (ASTPrimaryPrefix) primaryExpression.getChild(0); + } + + // check for static String::format calls + // Tree: Expression/PrimaryExpression/PrimaryPrefix/Name + if (primaryPrefix != null && primaryPrefix.getNumChildren() > 0 && primaryPrefix.getChild(0) instanceof ASTName) { + String name = primaryPrefix.getChild(0).getImage(); return "String.format".equals(name) || formatIsStringFormat && "format".equals(name); } + + // check for String::formatted calls - this method has been added with Java 15 + // Tree: //Expression/PrimaryExpression[PrimaryPrefix/Literal[@StringLiteral = true()]]/PrimarySuffix[@Image = 'formatted'] + if (primaryPrefix != null && primaryPrefix.getNumChildren() > 0 && primaryPrefix.getChild(0) instanceof ASTLiteral) { + ASTLiteral literal = (ASTLiteral) primaryPrefix.getChild(0); + if (literal.isStringLiteral() + && primaryExpression.getNumChildren() > 1 && primaryExpression.getChild(1) instanceof ASTPrimarySuffix) { + ASTPrimarySuffix primarySuffix = (ASTPrimarySuffix) primaryExpression.getChild(1); + return "formatted".equals(primarySuffix.getImage()); + } + } return false; } diff --git a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/InvalidLogMessageFormat.xml b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/InvalidLogMessageFormat.xml index e4c09ae77f..836e4adc1f 100644 --- a/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/InvalidLogMessageFormat.xml +++ b/pmd-java/src/test/resources/net/sourceforge/pmd/lang/java/rule/errorprone/xml/InvalidLogMessageFormat.xml @@ -1085,4 +1085,22 @@ public class Foo { } ]]> + + + [java] InvalidLogMessageFormat false positive on externally formatted strings #4172 + 0 + +