CINXE.COM
[RFC] Format attribute: `__attribute__((format_like(...)))` - Clang Frontend - LLVM Discussion Forums
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>[RFC] Format attribute: `__attribute__((format_like(...)))` - Clang Frontend - LLVM Discussion Forums</title> <meta name="description" content="A few years ago, I worked to apply __attribute__((format)) to non-variadic functions, plugging the most significant hole that was left at the time in the -Wformat-nonliteral story. I’m coming back today to get some feedb&hellip;"> <meta name="generator" content="Discourse 3.4.0.beta3-dev - https://github.com/discourse/discourse version 5bf5d1335680f28a8eb65c488353be9585eed08e"> <link rel="icon" type="image/png" href="https://global.discourse-cdn.com/flex015/uploads/llvm/optimized/1X/6ac54669f4b30cb07094375cbd65d24163b5b6e0_2_32x32.jpeg"> <link rel="apple-touch-icon" type="image/png" href="https://global.discourse-cdn.com/flex015/uploads/llvm/optimized/1X/6ac54669f4b30cb07094375cbd65d24163b5b6e0_2_180x180.jpeg"> <meta name="theme-color" media="all" content="#ffffff"> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=yes, viewport-fit=cover"> <link rel="canonical" href="https://discourse.llvm.org/t/rfc-format-attribute-attribute-format-like/83076" /> <link rel="search" type="application/opensearchdescription+xml" href="https://discourse.llvm.org/opensearch.xml" title="LLVM Discussion Forums Search"> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/color_definitions_base__2_97eb2726adc90ea788946f97df0eff9e96d11736.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" class="light-scheme"/> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/desktop_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="desktop" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/chat_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="chat" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/checklist_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="checklist" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-adplugin_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-adplugin" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-ai_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-ai" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-akismet_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-akismet" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-cakeday_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-cakeday" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-calendar_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-calendar" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-chat-integration_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-chat-integration" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-details_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-details" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-lazy-videos_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-lazy-videos" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-local-dates_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-local-dates" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-narrative-bot_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-narrative-bot" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-policy_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-policy" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-presence_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-presence" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-solved_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-solved" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-templates_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-templates" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-topic-voting_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-topic-voting" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/footnote_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="footnote" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/hosted-site_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="hosted-site" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/poll_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="poll" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/spoiler-alert_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="spoiler-alert" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/chat_desktop_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="chat_desktop" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-ai_desktop_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-ai_desktop" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-calendar_desktop_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-calendar_desktop" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/discourse-topic-voting_desktop_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="discourse-topic-voting_desktop" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/poll_desktop_689c3233f5745de1f03ddcb29a710e66164435f2.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="poll_desktop" /> <link href="https://sea1.discourse-cdn.com/flex015/stylesheets/desktop_theme_2_3ca050d7322651252d0449fb94b1704dbb200528.css?__ws=discourse.llvm.org" media="all" rel="stylesheet" data-target="desktop_theme" data-theme-id="2" data-theme-name="light"/> <script defer="" src="https://sea1.discourse-cdn.com/flex015/theme-javascripts/454dcd92185463cfae2259a66f283eea4ad30c97.js?__ws=discourse.llvm.org" data-theme-id="5" nonce="MeL5EP17PWHI2NzLZR7HlCf3c"></script> <link rel="alternate nofollow" type="application/rss+xml" title="RSS feed of '[RFC] Format attribute: `__attribute__((format_like(...)))`'" href="https://discourse.llvm.org/t/rfc-format-attribute-attribute-format-like/83076.rss" /> <meta property="og:site_name" content="LLVM Discussion Forums" /> <meta property="og:type" content="website" /> <meta name="twitter:card" content="summary" /> <meta name="twitter:image" content="https://global.discourse-cdn.com/flex015/uploads/llvm/original/1X/6ac54669f4b30cb07094375cbd65d24163b5b6e0.jpeg" /> <meta property="og:image" content="https://global.discourse-cdn.com/flex015/uploads/llvm/original/1X/6ac54669f4b30cb07094375cbd65d24163b5b6e0.jpeg" /> <meta property="og:url" content="https://discourse.llvm.org/t/rfc-format-attribute-attribute-format-like/83076" /> <meta name="twitter:url" content="https://discourse.llvm.org/t/rfc-format-attribute-attribute-format-like/83076" /> <meta property="og:title" content="[RFC] Format attribute: `__attribute__((format_like(...)))`" /> <meta name="twitter:title" content="[RFC] Format attribute: `__attribute__((format_like(...)))`" /> <meta property="og:description" content="A few years ago, I worked to apply __attribute__((format)) to non-variadic functions, plugging the most significant hole that was left at the time in the -Wformat-nonliteral story. I’m coming back today to get some feedback on improving format checking to make -Wformat-nonliteral yet more useful: accepting format strings given types that are not represented in the function signature. -Wformat-nonliteral warns when Clang is unable to infer the format specifiers used in a format string. For insta..." /> <meta name="twitter:description" content="A few years ago, I worked to apply __attribute__((format)) to non-variadic functions, plugging the most significant hole that was left at the time in the -Wformat-nonliteral story. I’m coming back today to get some feedback on improving format checking to make -Wformat-nonliteral yet more useful: accepting format strings given types that are not represented in the function signature. -Wformat-nonliteral warns when Clang is unable to infer the format specifiers used in a format string. For insta..." /> <meta property="og:article:section" content="Clang Frontend" /> <meta property="og:article:section:color" content="ED207B" /> <meta property="og:article:tag" content="clang" /> <meta property="article:published_time" content="2024-11-11T20:47:24+00:00" /> <meta property="og:ignore_canonical" content="true" /> </head> <body class="crawler browser-update"> <header> <a href="/"> LLVM Discussion Forums </a> </header> <div id="main-outlet" class="wrap" role="main"> <div id="topic-title"> <h1> <a href="/t/rfc-format-attribute-attribute-format-like/83076">[RFC] Format attribute: `__attribute__((format_like(...)))`</a> </h1> <div class="topic-category" itemscope itemtype="http://schema.org/BreadcrumbList"> <span itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem"> <a href="/c/clang/6" class="badge-wrapper bullet" itemprop="item"> <span class='badge-category-bg' style='background-color: #ED207B'></span> <span class='badge-category clear-badge'> <span class='category-name' itemprop='name'>Clang Frontend</span> </span> </a> <meta itemprop="position" content="1" /> </span> </div> <div class="topic-category"> <div class='discourse-tags list-tags'> <a href='https://discourse.llvm.org/tag/clang' class='discourse-tag' rel="tag">clang</a> </div> </div> </div> <div itemscope itemtype='http://schema.org/DiscussionForumPosting'> <meta itemprop='headline' content='[RFC] Format attribute: `__attribute__((format_like(...)))`'> <link itemprop='url' href='https://discourse.llvm.org/t/rfc-format-attribute-attribute-format-like/83076'> <meta itemprop='datePublished' content='2024-11-11T20:47:24Z'> <meta itemprop='articleSection' content='Clang Frontend'> <meta itemprop='keywords' content='clang'> <div itemprop='publisher' itemscope itemtype="http://schema.org/Organization"> <meta itemprop='name' content='LLVM Foundation'> <div itemprop='logo' itemscope itemtype="http://schema.org/ImageObject"> <meta itemprop='url' content='https://global.discourse-cdn.com/flex015/uploads/llvm/original/1X/6ac54669f4b30cb07094375cbd65d24163b5b6e0.jpeg'> </div> </div> <div id='post_1' class='topic-body crawler-post'> <div class='crawler-post-meta'> <span class="creator" itemprop="author" itemscope itemtype="http://schema.org/Person"> <a itemprop="url" href='https://discourse.llvm.org/u/fay59'><span itemprop='name'>fay59</span></a> </span> <link itemprop="mainEntityOfPage" href="https://discourse.llvm.org/t/rfc-format-attribute-attribute-format-like/83076"> <span class="crawler-post-infos"> <time datetime='2024-11-11T20:47:24Z' class='post-time'> November 11, 2024, 8:47pm </time> <meta itemprop='dateModified' content='2024-11-11T22:00:32Z'> <span itemprop='position'>1</span> </span> </div> <div class='post' itemprop='text'> <p>A few years ago, I worked to apply <code>__attribute__((format))</code> to non-variadic functions, plugging the most significant hole that was left at the time in the -Wformat-nonliteral story. I’m coming back today to get some feedback on improving format checking to make -Wformat-nonliteral yet more useful: accepting format strings given types that are not represented in the function signature.</p> <p>-Wformat-nonliteral warns when Clang is unable to infer the format specifiers used in a format string. For instance:</p> <pre data-code-wrap="c"><code class="lang-c">func myprintf(const char *str, ...) { va_list ap; va_start(ap, str); vprintf(str, ap); // warning: format string is not a string literal va_end(ap); } </code></pre> <p>We warn in this instance because we don’t know how to guarantee that <code>str</code> is a valid format string for the argument list. This specific instance is resolved by annotating <code>myprintf</code>:</p> <pre data-code-wrap="c"><code class="lang-c">__attribute__((format(printf, 1, 2))) func myprintf(const char *str, ...) { va_list ap; va_start(ap, str); vprintf(str, ap); // no diagnostic va_end(ap); } </code></pre> <p>Since call sites of <code>myprintf</code> will warn for incorrect format strings, the implementation of <code>myprintf</code> does not introduce format string unsafety and the diagnostic is not emitted.</p> <p>-Wformat-nonliteral is (was?) Clang community’s pet example of a diagnostic that wouldn’t be accepted by today’s standards. This is disappointing because format string problems are security problems, continue to surface every now and then, and they are <strong>preventable</strong>. A few years ago, I surveyed the reasons -Wformat-nonliteral triggers in our code bases and found the following categories, roughly in order of most prevalent to least prevalent:</p> <ol> <li>a format function is missing <code>__attribute__((format))</code></li> <li>the format string is localized</li> <li>the format string is unknown at the point of passing it, but it could be verified</li> <li>the format string formats formal arguments instead of variable arguments (for instance, <code>void error(const char *fmt, const char *file, int line)</code>)</li> <li>the format string is constructed at runtime to specialized precision fields</li> <li>the format string is a constant but it is held in a variable</li> </ol> <p>These are now all addressable:</p> <ol> <li>add <code>__attribute__((format))</code></li> <li>use <code>__attribute__((format_arg))</code></li> <li>verify the format string with <code>fmtcheck</code> (or your format string flavor’s equivalent), which can be implemented with <code>__attribute__((format_arg))</code></li> <li>as of clang-15, <code>__attribute__((format))</code> applies to functions without variadic arguments or fall back to solution <span class="hashtag-raw">#3</span></li> <li>use variable precision flags such as <code>printf("%*.*f", leading, decimals, value)</code> or fall back to solution <span class="hashtag-raw">#3</span></li> <li><code>const</code>ify the variable or fall back to solution <span class="hashtag-raw">#3</span></li> </ol> <p>(Note that although I’m only listing the busy work, as a result of these changes, we often find format string problems.)</p> <p>As you see, solution <span class="hashtag-raw">#3</span> can carry a lot of water. There’s one use case that it can cover but that I think we can have a better solution for: when a function accepts a format string with fixed arguments, but those arguments are not passed to the function. For instance:</p> <pre data-code-wrap="c"><code class="lang-c">const char *firstname; double temperature; void print_temperature(const char *format) { printf(format, firstname, temperature); } </code></pre> <p>This can be addressed today by using <code>printf(fmtcheck(format, "Hi %s, it's %g degrees outside"), temperature)</code>, but it defers a preventable error to runtime.</p> <p>I would like to propose a new attribute, <code>__attribute__((format_like))</code>, to check at compile-time that a format string is appropriate for a list of argument types. The syntax would be like <code>__attribute__((format))</code>'s, except that instead of indicating the first format argument, you would write out a format string that the argument should be compatible with:</p> <pre data-code-wrap="c"><code class="lang-c">__attribute__((format_like( flavor, format_string_param_index, format_string_literal))) </code></pre> <p>Used on <code>print_temperature</code>, it would look like this:</p> <pre data-code-wrap="c"><code class="lang-c">__attribute__((format_like(printf, 1, "%s %g"))) void print_temperature(const char *format); </code></pre> <p>With -Wformat, clang will then verify that callers of <code>print_temperature</code> pass a format string with specifiers compatible with “%s %g”. Inside the body of <code>print_temperature</code>, <code>format</code> will be assumed to have <code>%s %g</code> parameters for the purposes of verification.</p> <p>As an alternative to a format string literal, I thought of accepting a list of argument types. This makes it potentially more useful in the context of C++ callers that might want to create that list by template expansion. On the other hand, I don’t believe that this is supported at all today, and if this is the only motivation, it will never happen. Additionally, it creates substantially longer attribute spellings in the regular case, and it causes problems with -Wformat that assumes there’s a source location for the specifier to point to and propose fixits for. It’s also more likely to be something that other compilers can support without massively going out of their way.</p> <p>The bar to new attributes in clang is relatively low, but the area of format attributes in particular is already fairly crowded, so I wanted to preflight this with an RFC. In particular, there is partial overlap between <code>__attribute__((format))</code> used on non-variadic functions and <code>__attribute__((format_like))</code>. For instance, although they have slightly different failure modes for code that doesn’t work, they are equivalent in code that does work:</p> <pre data-code-wrap="c"><code class="lang-c">__attribute__((format(printf, 1, 2))) void print_windspeed(const char *fmt, const char *firstname, double speed); __attribute__((format_like(printf, 1, "%s %g"))) void print_windspeed(const char *fmt, const char *firstname, double speed); </code></pre> <p>However, there are still cases that can only realistically be served by <code>format</code> applying to non-variadic functions, such as C++ variadic template functions:</p> <pre data-code-wrap="c++"><code class="lang-c++">template<typename... Args> __attribute__((format(printf, 1, 2))) void print(const char *fmt, Args ...args); </code></pre> <p>In the end, I don’t think that it’s a problem that in some cases, you can get away with either. Also, GCC (as of 14.2) still does not accept the <code>format</code> attribute on non-variadic functions and makes it an error; folks that could get away with either could find it easier to work with <code>format_like</code> since you can either test for its existence with <code>__has_attribute(format_like)</code> or let GCC ignore it as unknown.</p> <p>Thoughts?</p> </div> <div itemprop="interactionStatistic" itemscope itemtype="http://schema.org/InteractionCounter"> <meta itemprop="interactionType" content="http://schema.org/LikeAction"/> <meta itemprop="userInteractionCount" content="0" /> <span class='post-likes'></span> </div> <div class='crawler-linkback-list' itemscope itemtype='http://schema.org/ItemList'> <div itemprop='itemListElement' itemscope itemtype='http://schema.org/ListItem'> <a itemprop='url' href="https://discourse.llvm.org/t/llvm-weekly-568-november-18th-2024/83214">LLVM Weekly - #568, November 18th 2024</a> <meta itemprop='position' content='1'> </div> </div> </div> <div id='post_2' itemprop='comment' itemscope itemtype='http://schema.org/Comment' class='topic-body crawler-post'> <div class='crawler-post-meta'> <span class="creator" itemprop="author" itemscope itemtype="http://schema.org/Person"> <a itemprop="url" href='https://discourse.llvm.org/u/erichkeane'><span itemprop='name'>erichkeane</span></a> </span> <span class="crawler-post-infos"> <time itemprop='datePublished' datetime='2024-11-12T14:26:06Z' class='post-time'> November 12, 2024, 2:26pm </time> <meta itemprop='dateModified' content='2024-11-12T14:26:06Z'> <span itemprop='position'>2</span> </span> </div> <div class='post' itemprop='text'> <p>On the surface, I don’t see an issue with an attribute like this, though I’m still having trouble with the motivation above.</p> <p>My biggest issue is the meaning of:<br> <code>pass a format string with specifiers compatible with “%s %g”</code></p> <p>If we mean ‘exactly those-in-that-order’, I think my implementation concerns are abated, though there are obvious portability/usability issues there.</p> <p>I guess my concern really is “what do we mean by compatible” here? Would we support a <code>%p</code> instead of a <code>%d</code>? Or vice-versa? etc.</p> </div> <div itemprop="interactionStatistic" itemscope itemtype="http://schema.org/InteractionCounter"> <meta itemprop="interactionType" content="http://schema.org/LikeAction"/> <meta itemprop="userInteractionCount" content="0" /> <span class='post-likes'></span> </div> </div> <div id='post_3' itemprop='comment' itemscope itemtype='http://schema.org/Comment' class='topic-body crawler-post'> <div class='crawler-post-meta'> <span class="creator" itemprop="author" itemscope itemtype="http://schema.org/Person"> <a itemprop="url" href='https://discourse.llvm.org/u/fay59'><span itemprop='name'>fay59</span></a> </span> <span class="crawler-post-infos"> <time itemprop='datePublished' datetime='2024-11-12T16:32:53Z' class='post-time'> November 12, 2024, 4:32pm </time> <meta itemprop='dateModified' content='2024-11-12T16:46:05Z'> <span itemprop='position'>3</span> </span> </div> <div class='post' itemprop='text'> <p>Thanks Erich. I’m using “compatible” in the same sense that a function with <code>format_arg</code> has to return a “compatible” format string (ie: roughly the same specifiers in the same place). The specifiers have to be in the same order. The general direction is to warn that two specifiers are incompatible if values that are valid for one specifier would be invalid for the other following -Wformat rules. <code>%p</code> is incompatible with <code>%d</code> even on platforms where pointers are <code>int</code>-sized because <code>printf("%p", 1)</code> emits a -Wformat diagnostic.</p> <p>Specifically, here’s the AttrDocs.td novella (where I already bikeshedded myself and decided that format_matches is a better name than format_like). There are a handful of other cases that don’t derive very well from this rule that I’m including at the bottom. For instance, <code>%p</code> is not compatible with <code>%s</code> because then you could use <code>%s</code> in a format string where <code>%p</code> is expected and we cannot guarantee that this is safe.</p> <hr> <p>The <code>format</code> attribute is the basis for the enforcement of diagnostics in the <code>-Wformat</code> family, but it only handles the case where the format string is passed along with the arguments it is going to format. It cannot handle the case where the format string and the format arguments are passed separately from each other. For instance:</p> <pre data-code-wrap="c"><code class="lang-c">static const char *first_name; static double todays_temperature; static int wind_speed; void say_hi(const char *fmt) { printf(fmt, first_name, todays_temperature); // warning: format string is not a string literal printf(fmt, first_name, wind_speed); // warning: format string is not a string literal } int main() { say_hi("hello %s, it is %g degrees outside"); say_hi("hello %s, it is %d degrees outside!"); // no diagnostic } </code></pre> <p>In this example, <code>fmt</code> is expected to format a <code>const char *</code> and a <code>double</code>, but these values are not passed to <code>say_hi</code>. Without the <code>format</code> attribute (which cannot apply in this case), the -Wformat-nonliteral diagnostic triggers in the body of <code>say_hi</code>, and incorrect <code>say_hi</code> call sites do not trigger a diagnostic.</p> <p>To complement the <code>format</code> attribute, Clang also defines the <code>format_matches</code> attribute. Its syntax is similar to the <code>format</code> attribute’s, but instead of taking the index of the first formatted value argument, it takes a C string literal with the expected specifiers:</p> <pre data-code-wrap="c"><code class="lang-c">static const char *first_name; static double todays_temperature; static int wind_speed; __attribute__((__format_matches__(printf, 1, "%s %g"))) void say_hi(const char *fmt) { printf(fmt, first_name, todays_temperature); // no dignostic printf(fmt, first_name, wind_speed); // warning: format specifies type 'int' but the argument has type 'double' } int main() { say_hi("hello %s, it is %3.1f degrees outside"); // no diagnostic say_hi("it is %g degrees outside, have a good day %s!"); // warning: format specifies 'double' where 'const char *' is required // warning: format specifies 'const char *' where 'double' is required } </code></pre> <p>The third argument to <code>format_matches</code> is expected to evaluate to a <strong>C string literal</strong> even when the format string would normally be a different type for the given flavor, like a <code>CFStringRef</code> or a <code>NSString *</code>.</p> <p>In the implementation of a function with the <code>format_matches</code> attribute, format verification works as if the format string was identical to the one specified in the attribute.</p> <p>At the call sites of functions with the <code>format_matches</code> attribute, format verification instead compares the two format strings to evaluate their equivalence. Each format flavor defines equivalence between format specifiers. Generally speaking, two specifiers are equivalent if they format values of the same type. For instance, in the <code>printf</code> flavor, <code>%2i</code> and <code>%-0.5d</code> are compatible. When <code>-Wformat-signedness</code> is disabled, <code>%d</code> and <code>%u</code> are compatible. For a negative example, <code>%ld</code> is incompatible with <code>%d</code>.</p> <p>Do note the following un-obvious cases:</p> <ul> <li>Passing <code>NULL</code> as the format string is accepted without diagnostics.</li> <li>When the format string is not NULL, it cannot <em>miss</em> specifiers, even in trailing positions. For instance, <code>%d</code> is not accepted when the required format is <code>%d %d %d</code>.</li> <li>Specifiers for integers as small or smaller than <code>int</code> (such as <code>%hhd</code>) are all mutually compatible because standard argument promotion ensures that integers are at least the size of <code>int</code> when passed as variadic arguments. With -Wformat-signedness, mixing specifier for types with a different signedness still results in a diagnostic.</li> <li>Format strings expecting a variable modifier (such as <code>%*s</code>) are incompatible with format strings that would itemize the variable modifiers (such as <code>%i %s</code>), even if the two specify ABI-compatible argument lists.</li> <li>All pointer specifiers, modifiers aside, are mutually incompatible. For instance, <code>%s</code> is not compatible with <code>%p</code>, and <code>%p</code> is not compatible with <code>%n</code>, and <code>%hhn</code> is incompatible with <code>%s</code>, even if the pointers are ABI-compatible or identical on the selected platform. However, <code>%0.5s</code> is compatible with <code>%s</code>, since the difference only exists in modifier flags. This is not overridable with <code>-Wformat-pedantic</code> or its inverse, which control similar behavior in <code>-Wformat</code>.</li> </ul> </div> <div itemprop="interactionStatistic" itemscope itemtype="http://schema.org/InteractionCounter"> <meta itemprop="interactionType" content="http://schema.org/LikeAction"/> <meta itemprop="userInteractionCount" content="0" /> <span class='post-likes'></span> </div> </div> <div id='post_4' itemprop='comment' itemscope itemtype='http://schema.org/Comment' class='topic-body crawler-post'> <div class='crawler-post-meta'> <span class="creator" itemprop="author" itemscope itemtype="http://schema.org/Person"> <a itemprop="url" href='https://discourse.llvm.org/u/erichkeane'><span itemprop='name'>erichkeane</span></a> </span> <span class="crawler-post-infos"> <time itemprop='datePublished' datetime='2024-11-25T14:47:29Z' class='post-time'> November 25, 2024, 2:47pm </time> <meta itemprop='dateModified' content='2024-11-25T14:47:29Z'> <span itemprop='position'>4</span> </span> </div> <div class='post' itemprop='text'> <p>I’m hopeful we can get more comments here… I have only just returned from WG21 (and this entire RFC was basically within the time that jsut about everyone was travelling), so haven’t done a deep dive on this yet (though I’ll review the patch soon!).</p> <p>I’d like to hear what others thing about this, so that we can eventually call consensus/approve this RFC.</p> </div> <div itemprop="interactionStatistic" itemscope itemtype="http://schema.org/InteractionCounter"> <meta itemprop="interactionType" content="http://schema.org/LikeAction"/> <meta itemprop="userInteractionCount" content="0" /> <span class='post-likes'></span> </div> </div> <div id='post_5' itemprop='comment' itemscope itemtype='http://schema.org/Comment' class='topic-body crawler-post'> <div class='crawler-post-meta'> <span class="creator" itemprop="author" itemscope itemtype="http://schema.org/Person"> <a itemprop="url" href='https://discourse.llvm.org/u/erichkeane'><span itemprop='name'>erichkeane</span></a> </span> <span class="crawler-post-infos"> <time itemprop='datePublished' datetime='2024-11-25T15:19:20Z' class='post-time'> November 25, 2024, 3:19pm </time> <meta itemprop='dateModified' content='2024-11-25T15:19:20Z'> <span itemprop='position'>5</span> </span> </div> <div class='post' itemprop='text'> <p>Alright, I read more closely this time, and I’m generally in favor. I am against the solution for <code>compatible</code>, and would prefer that we go with <code>identical</code> format strings.</p> <p>I’m not sure I completely grok the <code>-Wformat</code> rules, but basically: If you have a <code>format_matches</code> attribute, you must have the exact same format string ‘items’ in the exact same order (but can add modifiers I think?).</p> <p>SO basically: if you have a <code>%s %d</code>, you have to have exactly both of those, in that order, and no more in the <code>format_matches</code> attribute.</p> <p>Would that be acceptable for your use cases? I think we can examine requests to relax this in the future, but would rather we start as strict as possible. I see us trying to derive ‘compatible’ as being error-prone on various ABIs.</p> </div> <div itemprop="interactionStatistic" itemscope itemtype="http://schema.org/InteractionCounter"> <meta itemprop="interactionType" content="http://schema.org/LikeAction"/> <meta itemprop="userInteractionCount" content="0" /> <span class='post-likes'></span> </div> </div> <div id='post_6' itemprop='comment' itemscope itemtype='http://schema.org/Comment' class='topic-body crawler-post'> <div class='crawler-post-meta'> <span class="creator" itemprop="author" itemscope itemtype="http://schema.org/Person"> <a itemprop="url" href='https://discourse.llvm.org/u/AaronBallman'><span itemprop='name'>AaronBallman</span></a> </span> <span class="crawler-post-infos"> <time itemprop='datePublished' datetime='2024-11-26T18:30:22Z' class='post-time'> November 26, 2024, 6:30pm </time> <meta itemprop='dateModified' content='2024-11-26T18:30:22Z'> <span itemprop='position'>6</span> </span> </div> <div class='post' itemprop='text'> <aside class="quote no-group" data-username="fay59" data-post="3" data-topic="83076"> <div class="title"> <div class="quote-controls"></div> <img loading="lazy" alt="" width="24" height="24" src="https://sea1.discourse-cdn.com/flex015/user_avatar/discourse.llvm.org/fay59/48/16614_2.png" class="avatar"> fay59:</div> <blockquote> <p>Format strings expecting a variable modifier (such as <code>%*s</code>) are incompatible with format strings that would itemize the variable modifiers (such as <code>%i %s</code>), even if the two specify ABI-compatible argument lists.</p> </blockquote> </aside> <p>Do flags have to match? e.g., is it fine to use <code>%d</code> and <code>%+d</code> or are those non-matching?</p> <p>What about other differences like whitespace? e.g., are <code>%d %d</code> and <code>%d %d</code> fine?</p> <p>Or non-whitespace? e.g., is <code>%d toodles %d</code> acceptable, or can it only have format specifier characters?</p> <p>It’s turning out that most of my concerns are related to the fact that the format specifier string is being specified in two places and can have differences between them and those differences may matter. e.g., whitespace doesn’t matter in general, but it does for scanf specifiers including <code>[</code>, <code>c</code>, or <code>n</code> specifiers.</p> </div> <div itemprop="interactionStatistic" itemscope itemtype="http://schema.org/InteractionCounter"> <meta itemprop="interactionType" content="http://schema.org/LikeAction"/> <meta itemprop="userInteractionCount" content="0" /> <span class='post-likes'></span> </div> </div> <div id='post_7' itemprop='comment' itemscope itemtype='http://schema.org/Comment' class='topic-body crawler-post'> <div class='crawler-post-meta'> <span class="creator" itemprop="author" itemscope itemtype="http://schema.org/Person"> <a itemprop="url" href='https://discourse.llvm.org/u/erichkeane'><span itemprop='name'>erichkeane</span></a> </span> <span class="crawler-post-infos"> <time itemprop='datePublished' datetime='2024-11-26T18:33:41Z' class='post-time'> November 26, 2024, 6:33pm </time> <meta itemprop='dateModified' content='2024-11-26T18:33:41Z'> <span itemprop='position'>7</span> </span> </div> <div class='post' itemprop='text'> <p>My interpretation is that he uses ‘compatible’ in a way I disagree iwth, but whitespace/non-flags doesn’t matter, just the same/compatible flags in the same order.</p> </div> <div itemprop="interactionStatistic" itemscope itemtype="http://schema.org/InteractionCounter"> <meta itemprop="interactionType" content="http://schema.org/LikeAction"/> <meta itemprop="userInteractionCount" content="0" /> <span class='post-likes'></span> </div> </div> </div> <div id="related-topics" class="more-topics__list " role="complementary" aria-labelledby="related-topics-title"> <h3 id="related-topics-title" class="more-topics__list-title"> Related topics </h3> <div class="topic-list-container" itemscope itemtype='http://schema.org/ItemList'> <meta itemprop='itemListOrder' content='http://schema.org/ItemListOrderDescending'> <table class='topic-list'> <thead> <tr> <th>Topic</th> <th></th> <th class="replies">Replies</th> <th class="views">Views</th> <th>Activity</th> </tr> </thead> <tbody> <tr class="topic-list-item" id="topic-list-item-66487"> <td class="main-link" itemprop='itemListElement' itemscope itemtype='http://schema.org/ListItem'> <meta itemprop='position' content='1'> <span class="link-top-line"> <a itemprop='url' href='https://discourse.llvm.org/t/using-attribute-format-on-the-format-function/66487' class='title raw-link raw-topic-link'>Using `__attribute__((format))` on the `format` function</a> </span> <div class="link-bottom-line"> <a href='/c/llvm/5' class='badge-wrapper bullet'> <span class='badge-category-bg' style='background-color: #652D90'></span> <span class='badge-category clear-badge'> <span class='category-name'>LLVM Project</span> </span> </a> <div class="discourse-tags"> </div> </div> </td> <td class="replies"> <span class='posts' title='posts'>1</span> </td> <td class="views"> <span class='views' title='views'>672</span> </td> <td> November 9, 2022 </td> </tr> <tr class="topic-list-item" id="topic-list-item-31071"> <td class="main-link" itemprop='itemListElement' itemscope itemtype='http://schema.org/ListItem'> <meta itemprop='position' content='2'> <span class="link-top-line"> <a itemprop='url' href='https://discourse.llvm.org/t/feature-request-support-format-attribute-for-non-variadic-functions/31071' class='title raw-link raw-topic-link'>feature request: support format attribute for non-variadic functions</a> </span> <div class="link-bottom-line"> <a href='/c/clang/6' class='badge-wrapper bullet'> <span class='badge-category-bg' style='background-color: #ED207B'></span> <span class='badge-category clear-badge'> <span class='category-name'>Clang Frontend</span> </span> </a> <div class="discourse-tags"> </div> </div> </td> <td class="replies"> <span class='posts' title='posts'>0</span> </td> <td class="views"> <span class='views' title='views'>93</span> </td> <td> February 15, 2014 </td> </tr> <tr class="topic-list-item" id="topic-list-item-10091"> <td class="main-link" itemprop='itemListElement' itemscope itemtype='http://schema.org/ListItem'> <meta itemprop='position' content='3'> <span class="link-top-line"> <a itemprop='url' href='https://discourse.llvm.org/t/patch-format-attribute-with-function-pointers/10091' class='title raw-link raw-topic-link'>[PATCH] __format__ attribute with function pointers</a> </span> <div class="link-bottom-line"> <a href='/c/clang/6' class='badge-wrapper bullet'> <span class='badge-category-bg' style='background-color: #ED207B'></span> <span class='badge-category clear-badge'> <span class='category-name'>Clang Frontend</span> </span> </a> <div class="discourse-tags"> </div> </div> </td> <td class="replies"> <span class='posts' title='posts'>4</span> </td> <td class="views"> <span class='views' title='views'>87</span> </td> <td> March 25, 2008 </td> </tr> <tr class="topic-list-item" id="topic-list-item-54829"> <td class="main-link" itemprop='itemListElement' itemscope itemtype='http://schema.org/ListItem'> <meta itemprop='position' content='4'> <span class="link-top-line"> <a itemprop='url' href='https://discourse.llvm.org/t/rfc-new-format-string-attributes/54829' class='title raw-link raw-topic-link'>[RFC] new format string attributes</a> </span> <div class="link-bottom-line"> <a href='/c/clang/6' class='badge-wrapper bullet'> <span class='badge-category-bg' style='background-color: #ED207B'></span> <span class='badge-category clear-badge'> <span class='category-name'>Clang Frontend</span> </span> </a> <div class="discourse-tags"> </div> </div> </td> <td class="replies"> <span class='posts' title='posts'>1</span> </td> <td class="views"> <span class='views' title='views'>94</span> </td> <td> March 24, 2020 </td> </tr> <tr class="topic-list-item" id="topic-list-item-9582"> <td class="main-link" itemprop='itemListElement' itemscope itemtype='http://schema.org/ListItem'> <meta itemprop='position' content='5'> <span class="link-top-line"> <a itemprop='url' href='https://discourse.llvm.org/t/vprintf-3-and-format-string-is-not-a-string-literal/9582' class='title raw-link raw-topic-link'>vprintf(3) and "format string is not a string literal"</a> </span> <div class="link-bottom-line"> <a href='/c/clang/6' class='badge-wrapper bullet'> <span class='badge-category-bg' style='background-color: #ED207B'></span> <span class='badge-category clear-badge'> <span class='category-name'>Clang Frontend</span> </span> </a> <div class="discourse-tags"> </div> </div> </td> <td class="replies"> <span class='posts' title='posts'>2</span> </td> <td class="views"> <span class='views' title='views'>87</span> </td> <td> December 17, 2007 </td> </tr> </tbody> </table> </div> </div> </div> <footer class="container wrap"> <nav class='crawler-nav'> <ul> <li itemscope itemtype='http://schema.org/SiteNavigationElement'> <span itemprop='name'> <a href='/' itemprop="url">Home </a> </span> </li> <li itemscope itemtype='http://schema.org/SiteNavigationElement'> <span itemprop='name'> <a href='/categories' itemprop="url">Categories </a> </span> </li> <li itemscope itemtype='http://schema.org/SiteNavigationElement'> <span itemprop='name'> <a href='/guidelines' itemprop="url">Guidelines </a> </span> </li> <li itemscope itemtype='http://schema.org/SiteNavigationElement'> <span itemprop='name'> <a href='/tos' itemprop="url">Terms of Service </a> </span> </li> <li itemscope itemtype='http://schema.org/SiteNavigationElement'> <span itemprop='name'> <a href='/privacy' itemprop="url">Privacy Policy </a> </span> </li> </ul> </nav> <p class='powered-by-link'>Powered by <a href="https://www.discourse.org">Discourse</a>, best viewed with JavaScript enabled</p> </footer> <div class="buorg"><div>Unfortunately, <a href="https://www.discourse.org/faq/#browser">your browser is unsupported</a>. Please <a href="https://browsehappy.com">switch to a supported browser</a> to view rich content, log in and reply.</div></div> </body> </html>