[java] InvalidLogMessageFormat - check for formatted calls

Fixes #4172
This commit is contained in:
Andreas Dangel
2022-11-12 19:26:23 +01:00
parent 4ef342cd28
commit 69ccb693c0
3 changed files with 52 additions and 5 deletions

View File

@@ -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

View File

@@ -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;
}

View File

@@ -1085,4 +1085,22 @@ public class Foo {
}
]]></code>
</test-code>
<test-code>
<description>[java] InvalidLogMessageFormat false positive on externally formatted strings #4172</description>
<expected-problems>0</expected-problems>
<code><![CDATA[
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Foo {
private static final Logger LOGGER = LoggerFactory.getLogger(Foo.class);
public void bar() {
String msg = "Got kafka msg, headers=%s, body=%s".formatted(headers, new String(body, UTF_8));
LOGGER.info(msg);
return msg;
}
}
]]></code>
</test-code>
</test-data>