2023-05-03 11:06:20 +02:00
== Why is this an issue?
2024-09-13 14:48:30 +02:00
A https://learn.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting[composite format string] is a string that contains placeholders, represented by indices inside curly braces "{0}", "{1}", etc. These placeholders are replaced by values when the string is printed or logged.
2020-12-21 15:38:52 +01:00
2023-10-18 15:35:51 +02:00
Because composite format strings are interpreted at runtime, rather than validated by the compiler, they can contain errors that lead to unexpected behaviors or runtime errors.
This rule validates the correspondence between arguments and composite formats when calling the
following methods:
2024-09-13 14:48:30 +02:00
* https://learn.microsoft.com/en-us/dotnet/api/system.string.format[`String.Format`]
* https://learn.microsoft.com/en-us/dotnet/api/system.text.stringbuilder.appendformat[`StringBuilder.AppendFormat`]
* https://learn.microsoft.com/en-us/dotnet/api/system.console.write[`Console.Write`]
* https://learn.microsoft.com/en-us/dotnet/api/system.console.writeline[`Console.WriteLine`]
* https://learn.microsoft.com/en-us/dotnet/api/system.io.textwriter.write[`TextWriter.Write`]
* https://learn.microsoft.com/en-us/dotnet/api/system.io.textwriter.writeline[`TextWriter.WriteLine`]
* https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.debug.writeline[`Debug.WriteLine(String, Object[\])`]
* https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.trace.traceerror[`Trace.TraceError(String, Object[\])`]
* https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.trace.traceinformation[`Trace.TraceInformation(String, Object[\])`]
* https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.trace.tracewarning[`Trace.TraceWarning(String, Object[\])`]
* https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.tracesource.traceinformation[`TraceSource.TraceInformation(String, Object[\])`]
2023-10-18 15:35:51 +02:00
=== Exceptions
* No issue is raised if the format string is not a string literal, but comes from a variable.
2020-12-21 15:38:52 +01:00
2022-02-04 17:28:24 +01:00
[source,csharp]
2020-12-21 15:38:52 +01:00
----
2023-10-18 15:35:51 +02:00
var pattern = "{0} {1} {2}";
var res = string.Format(pattern, 1, 2); // Incorrect, but the analyzer doesn't raise any warnings here
2020-12-21 15:38:52 +01:00
----
2023-10-18 15:35:51 +02:00
* No issue is raised if the argument is not an inline created array.
2020-12-21 15:38:52 +01:00
2022-02-04 17:28:24 +01:00
[source,csharp]
2020-12-21 15:38:52 +01:00
----
2023-10-18 15:35:51 +02:00
var array = new int[] {};
var res = string.Format("{0} {1}", array); // Compliant; we don't know the size of the array
2020-12-21 15:38:52 +01:00
----
2023-10-18 15:35:51 +02:00
* This rule doesn't check whether the format specifier (defined after the `:`) is actually valid.
== How to fix it
A composite format string contains placeholders, replaced by values when the string is printed or logged. Mismatch in the format specifiers and the arguments provided can lead to incorrect strings being created.
To avoid issues, a developer should ensure that the provided arguments match format specifiers.
Moreover, use https://learn.microsoft.com/en-us/dotnet/csharp/tutorials/string-interpolation[string interpolation] when possible.
2020-12-21 15:38:52 +01:00
2023-10-18 15:35:51 +02:00
Instead of
2020-12-21 15:38:52 +01:00
2023-05-25 14:18:12 +02:00
[source,csharp]
2020-12-21 15:38:52 +01:00
----
2023-10-18 15:35:51 +02:00
string str = string.Format("Hello {0} {1}!", firstName, lastName);
2020-12-21 15:38:52 +01:00
----
2023-10-18 15:35:51 +02:00
use
2020-12-21 15:38:52 +01:00
2023-05-25 14:18:12 +02:00
[source,csharp]
2020-12-21 15:38:52 +01:00
----
2023-10-18 15:35:51 +02:00
string str = $"Hello {firstName} {lastName}!";
----
With string interpolation:
* the arguments are validated at compilation time rather than runtime
* modern code editors provide auto-completion when typing the interpolation expression
=== Code examples
==== Noncompliant code example
[source,csharp,diff-id=1,diff-type=noncompliant]
----
s = string.Format("{0}", arg0, arg1); // Noncompliant, arg1 is declared but not used.
s = string.Format("{0} {2}", arg0, arg1, arg2); // Noncompliant, the format item with index 1 is missing, so arg1 will not be used.
s = string.Format("foo"); // Noncompliant; there is no need to use "string.Format" here.
2020-12-21 15:38:52 +01:00
----
2023-10-18 15:35:51 +02:00
==== Compliant solution
2020-12-21 15:38:52 +01:00
2023-10-18 15:35:51 +02:00
[source,csharp,diff-id=1,diff-type=compliant]
----
s = string.Format("{0}", arg0);
s = string.Format("{0} {1}", arg0, arg2);
s = "foo";
----
2021-06-02 20:44:38 +02:00
2021-06-03 09:05:38 +02:00
ifdef::env-github,rspecator-view[]
2021-09-20 15:38:42 +02:00
'''
== Implementation Specification
(visible only on this page)
2021-06-08 15:52:13 +02:00
'''
2021-06-02 20:44:38 +02:00
== Comments And Links
(visible only on this page)
include::../comments-and-links.adoc[]
2023-06-22 10:38:01 +02:00
2021-06-03 09:05:38 +02:00
endif::env-github,rspecator-view[]