Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement FormatString for Java 15 String#formatted #2798

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -17,13 +17,15 @@
package com.google.errorprone.bugpatterns.formatstring;

import static com.google.errorprone.BugPattern.SeverityLevel.ERROR;
import static com.google.errorprone.matchers.method.MethodMatchers.instanceMethod;

import com.google.common.collect.ImmutableList;
import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker;
import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.util.ASTHelpers;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.MethodInvocationTree;
Expand All @@ -33,16 +35,40 @@
@BugPattern(name = "FormatString", summary = "Invalid printf-style format string", severity = ERROR)
public class FormatString extends BugChecker implements MethodInvocationTreeMatcher {

private static final Matcher<ExpressionTree> FORMATTED_METHOD = instanceMethod().onExactClass("java.lang.String").named("formatted");

@Override
public Description matchMethodInvocation(MethodInvocationTree tree, final VisitorState state) {
ImmutableList<ExpressionTree> args = FormatStringUtils.formatMethodArguments(tree, state);
if (args.isEmpty()) {
return Description.NO_MATCH;
}
ImmutableList<ExpressionTree> args;
MethodSymbol sym = ASTHelpers.getSymbol(tree);
if (sym == null) {
return Description.NO_MATCH;
}

if (FORMATTED_METHOD.matches(tree, state)) {
/*
Java 15 and greater supports the formatted method on an instance of string. If found
then use the string value as the pattern and all-of-the arguments and send directly to
the validate method.
*/
ExpressionTree receiver = ASTHelpers.getReceiver(tree);
if (receiver == null) {
// an unqualified call to 'formatted', possibly inside the definition
// of java.lang.String
return Description.NO_MATCH;
}
args =
ImmutableList.<ExpressionTree>builder()
.add(receiver)
.addAll(tree.getArguments())
.build();

} else {
args = FormatStringUtils.formatMethodArguments(tree, state);
}
if (args.isEmpty()) {
return Description.NO_MATCH;
}
FormatStringValidation.ValidationResult result =
FormatStringValidation.validate(sym, args, state);
if (result == null) {
Expand Down
Expand Up @@ -365,4 +365,47 @@ public void invalidIndex() {
"}")
.doTest();
}

@Test
public void stringFormattedNegativeCase() {
assumeTrue(RuntimeVersion.isAtLeast15());
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void f() {",
" \"%s %s\".formatted(\"foo\", \"baz\");",
" }",
"}")
.doTest();
}

@Test
public void stringFormattedPositiveCase() {
assumeTrue(RuntimeVersion.isAtLeast15());
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void f() {",
" // BUG: Diagnostic contains: missing argument for format specifier",
" \"%s %s %s\".formatted(\"foo\", \"baz\");",
" }",
"}")
.doTest();
}
cushon marked this conversation as resolved.
Show resolved Hide resolved

@Test
public void nonConstantStringFormattedNegativeCase() {
assumeTrue(RuntimeVersion.isAtLeast15());
compilationHelper
.addSourceLines(
"Test.java",
"class Test {",
" void f(String f) {",
" f.formatted(\"foo\", \"baz\");",
" }",
"}")
.doTest();
}
}