Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -3019,7 +3019,7 @@ class Sema final : public SemaBase {
llvm::SmallBitVector &CheckedVarArgs);
bool CheckFormatArguments(ArrayRef<const Expr *> Args,
FormatArgumentPassingKind FAPK,
const StringLiteral *ReferenceFormatString,
StringLiteral *ReferenceFormatString,
unsigned format_idx, unsigned firstDataArg,
FormatStringType Type, VariadicCallType CallType,
SourceLocation Loc, SourceRange range,
Expand Down
103 changes: 73 additions & 30 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "clang/AST/TypeLoc.h"
#include "clang/AST/UnresolvedSet.h"
#include "clang/Basic/AddressSpaces.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Basic/IdentifierTable.h"
Expand Down Expand Up @@ -7012,15 +7013,28 @@ bool Sema::CheckFormatString(const FormatMatchesAttr *Format,
return false;
}

static void CheckMissingFormatAttributes(Sema *S, FormatStringType FormatType,
unsigned FormatIdx, unsigned FirstArg,
ArrayRef<const Expr *> Args,
Sema::FormatArgumentPassingKind APK,
unsigned CallerParamIdx,
SourceLocation Loc) {
std::string escapeFormatString(StringRef Input) {
std::string Result;
llvm::raw_string_ostream OS(Result);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fine, but OS.write_escaped does something pretty similar to this loop.

for (char C : Input) {
StringRef Escaped = escapeCStyle<EscapeChar::Double>(C);
if (Escaped.empty())
OS << C;
else
OS << Escaped;
}
return Result;
}

static void CheckMissingFormatAttributes(
Sema *S, ArrayRef<const Expr *> Args, Sema::FormatArgumentPassingKind APK,
StringLiteral *ReferenceFormatString, unsigned FormatIdx,
unsigned FirstDataArg, FormatStringType FormatType, unsigned CallerParamIdx,
SourceLocation Loc) {
NamedDecl *Caller = S->getCurFunctionOrMethodDecl();
if (!Caller)
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will always fail in Objective-C because ObjCMethodDecl does not inherit from FunctionDecl. Would you add an Objective-C test to ensure this works? If you're not super familiar with it, the declaration/use syntax should be:

#include <stdarg.h>

@interface Foo
-(void)myprintf:(const char *)fmt, ... __attribute__((format(printf, 1, 2)));
-(void)myvprintf:(const char *)fmt list:(va_list)ap __attribute__((format(printf, 1, 0)));
@end

void bar(Foo *f) {
    [f myprintf:"hello %i", 123];
}

void baz(Foo *f, const char *fmt, va_list ap) {
    [f myvprintf:fmt list:ap];
}

Happy to answer other questions you may have.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this also always fails for blocks, but it matters less because __attribute__((format)) is sugar so adding the attribute on the block type doesn't guarantee that the block you receive also has it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar with objective-c and there doesn't seem to be much documentation regarding attributes, but I think I have a working implementation. Just one question: is there an implicit 'this' argument on obj-c methods?

I'll have to look into blocks to see how they fit in here.

Copy link
Contributor

@apple-fcloutier apple-fcloutier Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Objective-C methods have two implicit arguments (self and _cmd), but IIRC index 1 corresponds to the first explicit argument (ie, not like how it's done for C++).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added support for Objective-C. Let me know if there are other cases I should test. Also, I wasn't sure which attribute syntax is supported, so I used the GNU extension.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to cover it, thank you!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested where Objective-C tolerates attributes, and the fixit is incorrect currently:

#include <stdarg.h>

__attribute__((format(printf, 1, 0)))
int vprintf(const char *, va_list ap);

@interface Foo
-(void)printf:(const char *)fmt, ...;
@end

@implementation Foo
-(void)printf:(const char *)fmt, ...  {
	va_list ap;
	va_start(ap, fmt);
	vprintf(fmt, ap);
	va_end(ap);
}
@end

The fixit will suggest __attribute__((format(printf, 1, 2))) -(void)printf:(const char *)fmt, ... {, which is a syntax error. Attributes go at the end of the declaration, so it should be -(void)printf:(const char *)fmt, ... __attribute__((...)).

To make it as effective as possible, the fixit should also go to the canonical declaration, as returned by getCanonicalDecl(), which seems to do the right thing for both FunctionDecl (returns the first decl) and ObjCMethodDecl (finds the decl in the relevant @interface block).

Here's a patch for your change which I think does the trick (but that I didn't test extensively): objc-placement.patch

Caller = dyn_cast<NamedDecl>(Caller->getCanonicalDecl());

unsigned NumCallerParams = getFunctionOrMethodNumParams(Caller);

Expand All @@ -7038,13 +7052,13 @@ static void CheckMissingFormatAttributes(Sema *S, FormatStringType FormatType,
// function. Try to match caller and callee arguments. If successful, then
// emit a diag with the caller idx, otherwise we can't determine the callee
// arguments.
unsigned NumCalleeArgs = Args.size() - FirstArg;
unsigned NumCalleeArgs = Args.size() - FirstDataArg;
if (NumCalleeArgs == 0 || NumCallerParams < NumCalleeArgs) {
// There aren't enough arguments in the caller to pass to callee.
return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible in this case that one could use __attribute__((format_matches)) instead, which I believe I landed between your last PR and this one. I haven't thought very deeply about whether you have all the information you need here to do it, though, so there's a decent likelihood that you would look at it and decide it's not actually possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have to generate a format string for format_matches which I guess should be doable based on the argument types in the call, unless some non-scalar type is used.

}
for (unsigned CalleeIdx = Args.size() - 1, CallerIdx = NumCallerParams - 1;
CalleeIdx >= FirstArg; --CalleeIdx, --CallerIdx) {
CalleeIdx >= FirstDataArg; --CalleeIdx, --CallerIdx) {
const auto *Arg =
dyn_cast<DeclRefExpr>(Args[CalleeIdx]->IgnoreParenCasts());
if (!Arg)
Expand All @@ -7064,40 +7078,68 @@ static void CheckMissingFormatAttributes(Sema *S, FormatStringType FormatType,
: 0;
break;
case Sema::FormatArgumentPassingKind::FAPK_Elsewhere:
// Args are not passed to the callee.
return;
// The callee has a format_matches attribute. We will emit that instead.
if (!ReferenceFormatString)
return;
break;
}

// Emit the diagnostic and fixit.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a recovery action, should we also add an implicit format attribute to Caller? It might catch issues in the rest of the file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That changes some behavior:

void foo(const char *fmt, va_list args) {
  vprintf(fmt, args);
  vscanf(fmt, args);
}

Previously, this would emit two diagnostics to suggest a printf and scanf format attribute. If we add an implicit attr, then when looking at vscanf we would get passing 'printf' format string where 'scanf' format string is expected. Also, this would eliminate duplicate diagnostics created by multiple format function calls of the same type.

I'll see if there are any other differences before updating the PR, but these changes make sense to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These changes also make sense to me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, one thing to mention is that we should only add the attribute if the diagnostic is enabled at the call site, otherwise people will start seeing inexplicable format warnings that cannot be silenced. This would probably be a net positive, but the respectful way to do this would be to enable the diagnostic by default.

unsigned FormatStringIndex = CallerParamIdx + CallerArgumentIndexOffset;
StringRef FormatTypeName = S->GetFormatStringTypeName(FormatType);
{
std::string Attr =
("format(" + FormatTypeName + ", " + llvm::Twine(FormatStringIndex) +
", " + llvm::Twine(FirstArgumentIndex) + ")")
.str();
do {
std::string Attr, Fixit;
if (APK != Sema::FormatArgumentPassingKind::FAPK_Elsewhere)
llvm::raw_string_ostream(Attr)
<< "format(" << FormatTypeName << ", " << FormatStringIndex << ", "
<< FirstArgumentIndex << ")";
else
llvm::raw_string_ostream(Attr)
<< "format_matches(" << FormatTypeName << ", " << FormatStringIndex
<< ", \"" << escapeFormatString(ReferenceFormatString->getString())
<< "\")";
auto DB = S->Diag(Loc, diag::warn_missing_format_attribute)
<< Attr << Caller;
const LangOptions &LO = S->getLangOpts();
StringRef AttrPrefix, AttrSuffix;
if (LO.C23 || LO.CPlusPlus11) {
AttrPrefix = "[[gnu::";
AttrSuffix = "]] ";
} else if (LO.ObjC || LO.GNUMode) {
AttrPrefix = "__attribute__((";
AttrSuffix = ")) ";

SourceLocation SL;
llvm::raw_string_ostream IS(Fixit);
// The attribute goes at the start of the declaration in C/C++ functions
// and methods, but after the declaration for Objective-C methods.
if (isa<ObjCMethodDecl>(Caller)) {
IS << ' ';
SL = Caller->getEndLoc();
}
if (!AttrPrefix.empty()) {
DB << FixItHint::CreateInsertion(Caller->getBeginLoc(),
(AttrPrefix + Attr + AttrSuffix).str());
const LangOptions &LO = S->getLangOpts();
if (LO.C23 || LO.CPlusPlus11)
IS << "[[gnu::" << Attr << "]]";
else if (LO.ObjC || LO.GNUMode)
IS << "__attribute__((" << Attr << "))";
else
break;
if (!isa<ObjCMethodDecl>(Caller)) {
IS << ' ';
SL = Caller->getBeginLoc();
}
}
IS.flush();

DB << FixItHint::CreateInsertion(SL, Fixit);
} while (false);
S->Diag(Caller->getLocation(), diag::note_entity_declared_at) << Caller;

if (APK != Sema::FormatArgumentPassingKind::FAPK_Elsewhere) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should only do this if diag::warn_missing_format_attribute is enabled because it can cause new diagnostics to cascade, which users would have no way to disable. You can check with !Diag.isIgnored(diag::warn_missing_format_attribute, Loc).

Caller->addAttr(FormatAttr::CreateImplicit(
S->getASTContext(), &S->getASTContext().Idents.get(FormatTypeName),
FormatStringIndex, FirstArgumentIndex));
} else {
Caller->addAttr(FormatMatchesAttr::CreateImplicit(
S->getASTContext(), &S->getASTContext().Idents.get(FormatTypeName),
FormatStringIndex, ReferenceFormatString));
}
}

bool Sema::CheckFormatArguments(ArrayRef<const Expr *> Args,
Sema::FormatArgumentPassingKind APK,
const StringLiteral *ReferenceFormatString,
StringLiteral *ReferenceFormatString,
unsigned format_idx, unsigned firstDataArg,
FormatStringType Type,
VariadicCallType CallType, SourceLocation Loc,
Expand Down Expand Up @@ -7153,8 +7195,9 @@ bool Sema::CheckFormatArguments(ArrayRef<const Expr *> Args,

const LangOptions &LO = getLangOpts();
if (CallerParamIdx && (LO.GNUMode || LO.C23 || LO.CPlusPlus11))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should warn in all cases and use the language mode only to decide whether to offer a fixit.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue I see w/ that is that for language modes that don’t support [[]]-style attributes, there is no way to actually make the warning go away in a portable manner (you can use a #pragma but that’s a bit sad and also not portable).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should warn in all cases and use the language mode only to decide whether to offer a fixit.

I'm not sure if the warning that suggests adding an attribute makes sense if there isn't a portable way to actually add the attribute.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are portable ways to annotate your functions, it's just that you can't offer them as fixits for a variety of reasons (for instance, create a macro that adds the format attribute based on a __has_attribute test); in fact, even when a language-defined syntax is available, it might not be the preferred way to add the attribute in any given project.

But even then, I disagree that the only value (or even the main value) of any diagnostic is the fixit. Clang doesn't only diagnose situations that it knows how to fix. For a codebase that truly can't do anything about missing format attributes, the solution is to not take the intentional step of enabling the diagnostic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, the user can figure out a way to write the attribute in a suitable way. I have changed the diagnostic message to include the attribute parameters as well like:

diagnostic behavior may be improved by adding the 'format(printf, X, Y)' attribute to the declaration of 'foo'

so even if there is no fixit, the full suggested attribute is still shown.

CheckMissingFormatAttributes(this, Type, format_idx, firstDataArg, Args,
APK, *CallerParamIdx, Loc);
CheckMissingFormatAttributes(this, Args, APK, ReferenceFormatString,
format_idx, firstDataArg, Type,
*CallerParamIdx, Loc);

// Strftime is particular as it always uses a single 'time' argument,
// so it is safe to pass a non-literal string.
Expand Down
116 changes: 63 additions & 53 deletions clang/test/Sema/attr-format-missing.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ size_t strftime(char *, size_t, const char *, const struct tm *);
[[gnu::format(strfmon, 3, 4)]]
ssize_t strfmon(char *, size_t, const char *, ...);

[[gnu::format_matches(printf, 1, "%d %f \"'")]]
int custom_print(const char *, va_list);

// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 0)]] "
void f1(const char *fmt, va_list args) // #f1
{
Expand Down Expand Up @@ -85,31 +88,42 @@ void f7(char* out, ... /* args */)
vprintf("test", args);
}

// Ok, out is not passed to print functions.
void f8(char *out, va_list args)
// Ok, format string is not passed to format functions.
void f8(va_list args)
{
const char *ch = "format";
const char * const ch = "format";
vprintf(ch, args);
vprintf("test", args);
}

// Ok, out is not passed to scan functions.
void f9(va_list args)
{
const char *ch = "format";
vscanf(ch, args);
vscanf("test", args);

char out[10];

struct tm tm_arg;
tm_arg.i = 0;
strftime(out, sizeof(out), ch, &tm_arg);
strftime(out, sizeof(out), "test", &tm_arg);

strfmon(out, sizeof(out), ch);
strfmon(out, sizeof(out), "test");
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:1-[[@LINE+2]]:1}:"{{\[\[}}gnu::format(scanf, 1, 2)]] "
// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 2)]] "
// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format_matches(printf, 1, \"%d %f \\\"'\")]] "
void f9(const char *fmt, ...) // #f9
{
va_list args;
custom_print(fmt, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format_matches(printf, 1, "%d %f \"'")' attribute to the declaration of 'f9'}}
// expected-note@#f9 {{'f9' declared here}}
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(scanf, 1, 2)]] "
void f10(const char *out, ... /* args */) // #f10
{
va_list args;
vscanf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(scanf, 1, 2)' attribute to the declaration of 'f10'}}
// expected-note@#f10 {{'f10' declared here}}
vprintf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f10'}}
// expected-note@#f10 {{'f10' declared here}}
vprintf(out, args); // expected-warning {{passing 'scanf' format string where 'printf' format string is expected}}
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 2)]] "
Expand All @@ -132,87 +146,83 @@ void f12(char* out) // #f12
// expected-note@#f12 {{'f12' declared here}}
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:1-[[@LINE+2]]:1}:"{{\[\[}}gnu::format(scanf, 1, 2)]] "
// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 2)]] "
void f13(char *out, ... /* args */) // #f13
{
va_list args;
vscanf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(scanf, 1, 2)' attribute to the declaration of 'f13'}}
// expected-note@#f13 {{'f13' declared here}}
vprintf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f13'}}
// expected-note@#f13 {{'f13' declared here}}
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:1-[[@LINE+2]]:1}:"{{\[\[}}gnu::format(scanf, 1, 0)]] "
// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 0)]] "
void f14(char *out, va_list args) // #f14
void f13(char *out, va_list args) // #f13
{
vscanf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(scanf, 1, 0)' attribute to the declaration of 'f14'}}
// expected-note@#f14 {{'f14' declared here}}
vprintf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 0)' attribute to the declaration of 'f14'}}
// expected-note@#f14 {{'f14' declared here}}
vprintf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 0)' attribute to the declaration of 'f13'}}
// expected-note@#f13 {{'f13' declared here}}
vscanf(out, args); // expected-warning {{passing 'printf' format string where 'scanf' format string is expected}}
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(scanf, 1, 2)]] "
void f15(char *out, ... /* args */) // #f15
void f14(char *out, ... /* args */) // #f14
{
va_list args;
vscanf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(scanf, 1, 2)' attribute to the declaration of 'f15'}}
// expected-note@#f15 {{'f15' declared here}}
vscanf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(scanf, 1, 2)' attribute to the declaration of 'f15'}}
// expected-note@#f15 {{'f15' declared here}}
vscanf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(scanf, 1, 2)' attribute to the declaration of 'f14'}}
// expected-note@#f14 {{'f14' declared here}}
vscanf(out, args);
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+2]]:1-[[@LINE+2]]:1}:"{{\[\[}}gnu::format(printf, 1, 3)]] "
// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 2, 3)]] "
void f16(char *ch, const char *out, ... /* args */) // #f16
void f15(char *ch, const char *out, ... /* args */) // #f15
{
va_list args;
vprintf(ch, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 3)' attribute to the declaration of 'f16'}}
// expected-note@#f16 {{'f16' declared here}}
vprintf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'f16'}}
// expected-note@#f16 {{'f16' declared here}}
vprintf(ch, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 3)' attribute to the declaration of 'f15'}}
// expected-note@#f15 {{'f15' declared here}}
vprintf(out, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 2, 3)' attribute to the declaration of 'f15'}}
// expected-note@#f15 {{'f15' declared here}}
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 2)]] "
void f17(const char *a, ...) // #f17
void f16(const char *a, ...) // #f16
{
va_list ap;
const char *const b = a;
vprintf(b, ap); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f17'}}
// expected-note@#f17 {{'f17' declared here}}
vprintf(b, ap); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f16'}}
// expected-note@#f16 {{'f16' declared here}}
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 2)]] "
void f18(char *fmt, unsigned x, unsigned y, unsigned z) // #f18
void f17(char *fmt, unsigned x, unsigned y, unsigned z) // #f17
{
printf(fmt, x, y, z); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f18'}}
// expected-note@#f18 {{'f18' declared here}}
printf(fmt, x, y, z); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f17'}}
// expected-note@#f17 {{'f17' declared here}}
}

void f19(char *fmt, unsigned x, unsigned y, unsigned z) // #f19
void f18(char *fmt, unsigned x, unsigned y, unsigned z) // #f18
{
// Arguments are not passed in the same order.
printf(fmt, x, z, y);
}

void f20(char *out, ... /* args */)
void f19(char *out, ... /* args */)
{
printf(out, 1); // No warning, arguments are not passed to printf.
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(strftime, 3, 0)]] "
void f21(char *out, const size_t len, const char *format) // #f21
void f20(char *out, const size_t len, const char *format) // #f20
{
struct tm tm_arg;
tm_arg.i = 0;
strftime(out, len, format, &tm_arg); // expected-warning {{diagnostic behavior may be improved by adding the 'format(strftime, 3, 0)' attribute to the declaration of 'f21'}}
// expected-note@#f21 {{'f21' declared here}}
strftime(out, len, format, &tm_arg); // expected-warning {{diagnostic behavior may be improved by adding the 'format(strftime, 3, 0)' attribute to the declaration of 'f20'}}
// expected-note@#f20 {{'f20' declared here}}
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(strfmon, 3, 4)]] "
void f22(char *out, const size_t len, const char *format, int x, int y) // #f22
void f21(char *out, const size_t len, const char *format, int x, int y) // #f21
{
strfmon(out, len, format, x, y); // expected-warning {{diagnostic behavior may be improved by adding the 'format(strfmon, 3, 4)' attribute to the declaration of 'f21'}}
// expected-note@#f21 {{'f21' declared here}}
}

// CHECK: fix-it:"{{.*}}":{[[@LINE+1]]:1-[[@LINE+1]]:1}:"{{\[\[}}gnu::format(printf, 1, 2)]] "
void f22(const char *fmt, ... /* args */); // #f22

void f22(const char *fmt, ... /* args */)
{
strfmon(out, len, format, x, y); // expected-warning {{diagnostic behavior may be improved by adding the 'format(strfmon, 3, 4)' attribute to the declaration of 'f22'}}
// expected-note@#f22 {{'f22' declared here}}
va_list args;
vprintf(fmt, args); // expected-warning {{diagnostic behavior may be improved by adding the 'format(printf, 1, 2)' attribute to the declaration of 'f22'}}
// expected-note@#f22 {{'f22' declared here}}
}
Loading