
In some cases, the `rule.adoc` at root of a rule is never included anywhere and thus is dead code. It's a maintenance cost by itself, but also it misses opportunities to inline code that seems used by two documents when in fact only one document is actually rendered. And this missed opportunity, in turn, stops us from applying the correct language tag on the code samples.
90 lines
4.1 KiB
Plaintext
90 lines
4.1 KiB
Plaintext
== Why is this an issue?
|
|
|
|
Because ``++printf++``-style format strings are interpreted at runtime, rather than validated by the compiler, they can contain errors that result in the wrong strings being created. This rule statically validates the correlation of ``++printf++``-style format strings to their arguments when calling the ``++format(...)++`` methods of ``++java.util.Formatter++``, ``++java.lang.String++``, ``++java.io.PrintStream++``, ``++MessageFormat++``, and ``++java.io.PrintWriter++`` classes and the ``++printf(...)++`` methods of ``++java.io.PrintStream++`` or ``++java.io.PrintWriter++`` classes.
|
|
|
|
|
|
=== Noncompliant code example
|
|
|
|
[source,java]
|
|
----
|
|
String.format("First {0} and then {1}", "foo", "bar"); //Noncompliant. Looks like there is a confusion with the use of {{java.text.MessageFormat}}, parameters "foo" and "bar" will be simply ignored here
|
|
String.format("Display %3$d and then %d", 1, 2, 3); //Noncompliant; the second argument '2' is unused
|
|
String.format("Too many arguments %d and %d", 1, 2, 3); //Noncompliant; the third argument '3' is unused
|
|
String.format("First Line\n"); //Noncompliant; %n should be used in place of \n to produce the platform-specific line separator
|
|
String.format("Is myObject null ? %b", myObject); //Noncompliant; when a non-boolean argument is formatted with %b, it prints true for any nonnull value, and false for null. Even if intended, this is misleading. It's better to directly inject the boolean value (myObject == null in this case)
|
|
String.format("value is " + value); // Noncompliant
|
|
String s = String.format("string without arguments"); // Noncompliant
|
|
|
|
MessageFormat.format("Result '{0}'.", value); // Noncompliant; String contains no format specifiers. (quote are discarding format specifiers)
|
|
MessageFormat.format("Result {0}.", value, value); // Noncompliant; 2nd argument is not used
|
|
MessageFormat.format("Result {0}.", myObject.toString()); // Noncompliant; no need to call toString() on objects
|
|
|
|
java.util.Logger logger;
|
|
logger.log(java.util.logging.Level.SEVERE, "Result {0}.", myObject.toString()); // Noncompliant; no need to call toString() on objects
|
|
logger.log(java.util.logging.Level.SEVERE, "Result.", new Exception()); // compliant, parameter is an exception
|
|
logger.log(java.util.logging.Level.SEVERE, "Result '{0}'", 14); // Noncompliant - String contains no format specifiers.
|
|
logger.log(java.util.logging.Level.SEVERE, "Result " + param, exception); // Noncompliant; Lambda should be used to differ string concatenation.
|
|
|
|
org.slf4j.Logger slf4jLog;
|
|
org.slf4j.Marker marker;
|
|
|
|
slf4jLog.debug(marker, "message {}");
|
|
slf4jLog.debug(marker, "message", 1); // Noncompliant - String contains no format specifiers.
|
|
|
|
org.apache.logging.log4j.Logger log4jLog;
|
|
log4jLog.debug("message", 1); // Noncompliant - String contains no format specifiers.
|
|
----
|
|
|
|
|
|
=== Compliant solution
|
|
|
|
[source,java]
|
|
----
|
|
String.format("First %s and then %s", "foo", "bar");
|
|
String.format("Display %2$d and then %d", 1, 3);
|
|
String.format("Too many arguments %d %d", 1, 2);
|
|
String.format("First Line%n");
|
|
String.format("Is myObject null ? %b", myObject == null);
|
|
String.format("value is %d", value);
|
|
String s = "string without arguments";
|
|
|
|
MessageFormat.format("Result {0}.", value);
|
|
MessageFormat.format("Result '{0}' = {0}", value);
|
|
MessageFormat.format("Result {0}.", myObject);
|
|
|
|
java.util.Logger logger;
|
|
logger.log(java.util.logging.Level.SEVERE, "Result {0}.", myObject);
|
|
logger.log(java.util.logging.Level.SEVERE, "Result {0}'", 14);
|
|
logger.log(java.util.logging.Level.SEVERE, exception, () -> "Result " + param);
|
|
|
|
org.slf4j.Logger slf4jLog;
|
|
org.slf4j.Marker marker;
|
|
|
|
slf4jLog.debug(marker, "message {}");
|
|
slf4jLog.debug(marker, "message {}", 1);
|
|
|
|
org.apache.logging.log4j.Logger log4jLog;
|
|
log4jLog.debug("message {}", 1);
|
|
----
|
|
|
|
|
|
== Resources
|
|
|
|
* https://wiki.sei.cmu.edu/confluence/x/J9YxBQ[CERT, FIO47-C.] - Use valid format strings
|
|
|
|
ifdef::env-github,rspecator-view[]
|
|
|
|
'''
|
|
== Implementation Specification
|
|
(visible only on this page)
|
|
|
|
include::../message.adoc[]
|
|
|
|
'''
|
|
== Comments And Links
|
|
(visible only on this page)
|
|
|
|
include::../comments-and-links.adoc[]
|
|
|
|
endif::env-github,rspecator-view[]
|