EagerStringFormatting

WARNING

Performance

Simplification

View source code on GitHub

Summary

String formatting can be deferred

Suppression

Suppress false positives by adding the suppression annotation @SuppressWarnings("EagerStringFormatting") to the enclosing element.

Disable this pattern completely by adding -Xep:EagerStringFormatting:OFF as compiler argument. Learn more.

Samples

Replacement

Shows the difference in example code before and after the bug pattern is applied.

 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Verify.verify;
 import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
 import java.util.Locale;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.Marker;
 
 class A {
   private static final Logger LOG = LoggerFactory.getLogger(A.class);
   private static final String GUAVA_COMPATIBLE_PATTERN = "with-only-%s-placeholder";
   private static final String GUAVA_INCOMPATIBLE_PATTERN = "with-%%-marker";
 
   void m() {
-    requireNonNull("never-null", String.format("Format string: %s", 0));
+    requireNonNull("never-null", () -> String.format("Format string: %s", 0));
 
-    checkArgument(true, String.format("Vacuous format string %%"));
-    checkNotNull("never-null", "Format string: %s %s%%".formatted(1, 2));
-    checkState(false, String.format(Locale.US, "Format string with locale: %s", 3));
-    verify(true, GUAVA_COMPATIBLE_PATTERN.formatted(4));
-    verifyNotNull("never-null", String.format(Locale.ENGLISH, GUAVA_COMPATIBLE_PATTERN, 5));
-    checkArgument(false, GUAVA_INCOMPATIBLE_PATTERN.formatted());
-    checkNotNull("never-null", String.format(GUAVA_INCOMPATIBLE_PATTERN));
+    checkArgument(true, "Vacuous format string %");
+    checkNotNull("never-null", "Format string: %s %s%", 1, 2);
+    checkState(false, "Format string with locale: %s", 3);
+    verify(true, GUAVA_COMPATIBLE_PATTERN, 4);
+    verifyNotNull("never-null", GUAVA_COMPATIBLE_PATTERN, 5);
+    checkArgument(false, "with-%-marker");
+    checkNotNull("never-null", "with-%-marker");
 
-    LOG.trace("Vacuous format string %%".formatted());
-    LOG.debug(String.format("With format string: %s, %s%%", 6, 7));
-    LOG.info(String.format(Locale.ROOT, "With vacuous localized format string %%"));
-    LOG.warn((Marker) null, "With marker and format string: %s".formatted(8));
-    LOG.error(
-        String.format(Locale.US, "With throwable and format string: %s, %s", 9, 10),
-        new RuntimeException());
+    LOG.trace("Vacuous format string %");
+    LOG.debug("With format string: {}, {}%", 6, 7);
+    LOG.info("With vacuous localized format string %");
+    LOG.warn((Marker) null, "With marker and format string: {}", 8);
+    LOG.error("With throwable and format string: {}, {}", 9, 10, new RuntimeException());
     LOG.trace(
-        (Marker) null,
-        "With marker, throwable and format string: %s".formatted(11),
-        new RuntimeException());
-    LOG.debug(GUAVA_COMPATIBLE_PATTERN.formatted(12));
-    LOG.info(String.format(Locale.ENGLISH, GUAVA_COMPATIBLE_PATTERN, 13));
-    LOG.warn(GUAVA_INCOMPATIBLE_PATTERN.formatted());
-    LOG.error(String.format(GUAVA_INCOMPATIBLE_PATTERN));
+        (Marker) null, "With marker, throwable and format string: {}", 11, new RuntimeException());
+    LOG.debug("with-only-{}-placeholder", 12);
+    LOG.info("with-only-{}-placeholder", 13);
+    LOG.warn("with-%-marker");
+    LOG.error("with-%-marker");
   }
 }
 

Identification

Shows code lines which will (not) be flagged by this bug pattern.
A //BUG: Diagnostic contains: comment is placed above any violating line.

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Verify.verify;
import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;

import java.util.Formattable;
import java.util.Locale;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;

class A {
  private static final Logger LOG = LoggerFactory.getLogger(A.class);

  private int nonFinalField = 0;

  void m() {
    Formattable formattable = (formatter, flags, width, precision) -> {};
    int effectivelyFinalLocal = 0;
    /* A local variable that is also not effectively final. */
    int nonFinalLocal = 0;
    nonFinalLocal = 1;

    String.format("%s", "foo");
    String.format(Locale.US, "%s", "foo");
    "%s".formatted("foo");
    String.format("%s", "foo", "bar");
    String.format("%s %s", "foo", "bar");
    String.format("%s %s %%", "foo", "bar");

    System.out.println(String.format("%s", nonFinalLocal));

    requireNonNull("never-null");
    requireNonNull("never-null", () -> String.format("Format string: %s", nonFinalField));
    // BUG: Diagnostic matches: VACUOUS
    requireNonNull(String.format("Never-null format string: %s", nonFinalField));
    // BUG: Diagnostic matches: VACUOUS
    requireNonNull("Never-null format string: %s".formatted(nonFinalField), "message");
    // BUG: Diagnostic matches: VACUOUS
    requireNonNull(
        String.format("Never-null format string"), String.format("Malformed format string: %"));
    // BUG: Diagnostic matches: DEFER_EXTRA_VARIABLE
    requireNonNull("never-null", String.format("Format string: %s", nonFinalLocal));
    // BUG: Diagnostic matches: DEFER
    requireNonNull("never-null", String.format("Format string: %s", effectivelyFinalLocal));
    // BUG: Diagnostic matches: DEFER
    requireNonNull(
        "never-null",
        String.format(
            "Custom format string: %s, %d, %s", getClass(), nonFinalField, "string-constant"));

    checkArgument(true);
    checkNotNull("never-null");
    checkState(false);
    verify(true);
    verifyNotNull("never-null");
    checkArgument(false, "Without format string");
    checkNotNull("never-null", "Without format string");
    checkState(true, "Without format string");
    verify(false, "Without format string");
    verifyNotNull("never-null", "Without format string");
    checkArgument(true, "With format string: %s", nonFinalLocal);
    checkNotNull("never-null", "With format string: %s", nonFinalLocal);
    checkState(false, "With format string: %s", nonFinalLocal);
    verify(true, "With format string: %s", nonFinalLocal);
    verifyNotNull("never-null", "With format string: %s", nonFinalLocal);
    // BUG: Diagnostic matches: VACUOUS
    checkNotNull(String.format("Never-null format string: %s", nonFinalLocal));
    // BUG: Diagnostic matches: VACUOUS
    verifyNotNull("Never-null format string: %s".formatted(nonFinalLocal), "message");
    // BUG: Diagnostic matches: VACUOUS
    checkNotNull(
        String.format("Never-null format string"), String.format("Malformed format string: %"));
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA
    checkArgument(true, String.format(toString()));
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA
    checkNotNull("never-null", toString().formatted());
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA
    checkState(true, String.format("Custom format string: %d", nonFinalLocal));
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA
    verify(true, "Mismatched format string:".formatted(nonFinalLocal));
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA
    verifyNotNull("never-null", "Mismatched format string: %d".formatted());
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA
    checkArgument(true, String.format("Malformed format string: %"));
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA
    checkNotNull("never-null", "Format string with `Formattable`: %s".formatted(formattable));
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA
    checkState(true, String.format("Generated format string: %%s"), nonFinalLocal);
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_GUAVA
    verify(
        true,
        "Format string with format string argument: %s",
        String.format("Format string argument: %s", nonFinalLocal));
    // BUG: Diagnostic matches: DEFER
    verifyNotNull(
        "never-null", String.format("Format string: %s, %s", nonFinalLocal, nonFinalLocal));
    // BUG: Diagnostic matches: DEFER
    checkArgument(true, "Format string: %s%%".formatted(nonFinalLocal));
    // BUG: Diagnostic matches: DEFER
    checkNotNull(
        "never-null", String.format(Locale.US, "Format string with locale: %s", nonFinalLocal));

    LOG.trace("Without format string");
    LOG.debug("With format string: {}", nonFinalLocal);
    LOG.info((Marker) null, "With marker");
    LOG.warn((Marker) null, "With marker and format string: {}", nonFinalLocal);
    LOG.error("With throwable", new RuntimeException());
    LOG.trace("With throwable and format string: {}", nonFinalLocal, new RuntimeException());
    LOG.debug((Marker) null, "With marker and throwable", new RuntimeException());
    LOG.info(
        (Marker) null,
        "With marker, throwable and format string: {}",
        nonFinalLocal,
        new RuntimeException());
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J
    LOG.warn(String.format(toString()));
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J
    LOG.error(toString().formatted());
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J
    LOG.trace(String.format("Custom format string: %d", nonFinalLocal));
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J
    LOG.debug("Mismatched format string:".formatted(nonFinalLocal));
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J
    LOG.info("Mismatched format string %d:".formatted());
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J
    LOG.warn(String.format("Malformed format string: %"));
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J
    LOG.error("Format string with `Formattable`: %s".formatted(formattable));
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J
    LOG.trace(String.format("Generated format string: {}"), nonFinalLocal);
    // BUG: Diagnostic matches: DEFER_SIMPLIFIED_SLF4J
    LOG.debug(
        "Format string with format string argument: {}",
        String.format("Format string argument: %s", nonFinalLocal));
    // BUG: Diagnostic matches: DEFER
    LOG.info(String.format("Vacuous format string %%"));
    // BUG: Diagnostic matches: DEFER
    LOG.warn(String.format("With format string: %s, %s", nonFinalLocal, nonFinalLocal));
    // BUG: Diagnostic matches: DEFER
    LOG.error(String.format(Locale.ROOT, "With vacuous localized format string %%"));
    // BUG: Diagnostic matches: DEFER
    LOG.trace((Marker) null, String.format("With marker and format string: %s", nonFinalLocal));
    // BUG: Diagnostic matches: DEFER
    LOG.debug(
        String.format("With throwable and format string: %s", nonFinalLocal),
        new RuntimeException());
    // BUG: Diagnostic matches: DEFER
    LOG.info(
        (Marker) null,
        String.format("With marker, throwable and format string: %s", nonFinalLocal),
        new RuntimeException());
  }
}

Copyright © 2017-2024 Picnic Technologies BV