Skip to content

Commit 29fa8eb

Browse files
Improve diagnostics when compared format strings have different numbers of specifiers
1 parent fbe14c9 commit 29fa8eb

File tree

3 files changed

+33
-22
lines changed

3 files changed

+33
-22
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10018,8 +10018,8 @@ def note_previous_declaration_as : Note<
1001810018

1001910019
def warn_printf_insufficient_data_args : Warning<
1002010020
"more '%%' conversions than data arguments">, InGroup<FormatInsufficientArgs>;
10021-
def warn_format_cmp_insufficient_specifiers : Warning<
10022-
"fewer specifiers in format string than expected">, InGroup<FormatInsufficientArgs>;
10021+
def warn_format_cmp_specifier_arity : Warning<
10022+
"%select{fewer|more}0 specifiers in format string than expected">, InGroup<FormatInsufficientArgs>;
1002310023
def warn_printf_data_arg_not_used : Warning<
1002410024
"data argument not used by format string">, InGroup<FormatExtraArgs>;
1002510025
def warn_format_invalid_conversion : Warning<

clang/lib/Sema/SemaChecking.cpp

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8376,22 +8376,27 @@ bool CheckScanfHandler::HandleScanfSpecifier(
83768376
static void CompareFormatSpecifiers(Sema &S, const StringLiteral *Ref,
83778377
ArrayRef<EquatableFormatArgument> RefArgs,
83788378
const StringLiteral *Fmt,
8379-
ArrayRef<EquatableFormatArgument> FmtArgs) {
8379+
ArrayRef<EquatableFormatArgument> FmtArgs,
8380+
const Expr *FmtExpr, bool InFunctionCall) {
83808381
unsigned CommonArgs;
8381-
if (RefArgs.size() > FmtArgs.size()) {
8382-
CommonArgs = FmtArgs.size();
8383-
// "data argument not used by format string"
8384-
const auto &Arg = RefArgs[FmtArgs.size()];
8385-
S.Diag(Arg.getSourceLocation(), diag::warn_printf_data_arg_not_used)
8386-
<< Arg.getSourceRange();
8387-
S.Diag(Fmt->getBeginLoc(), diag::note_format_cmp_with) << 1;
8388-
} else if (FmtArgs.size() > RefArgs.size()) {
8382+
if (FmtArgs.size() > RefArgs.size()) {
83898383
CommonArgs = RefArgs.size();
8390-
// "more % conversions than data arguments"
8391-
const auto &Arg = FmtArgs[RefArgs.size()];
8392-
S.Diag(Fmt->getBeginLoc(), diag::warn_format_cmp_insufficient_specifiers)
8393-
<< Arg.getSourceRange();
8384+
// "data argument not used by format string"
8385+
CheckFormatHandler::EmitFormatDiagnostic(
8386+
S, InFunctionCall, FmtExpr,
8387+
S.PDiag(diag::warn_format_cmp_specifier_arity) << 1,
8388+
FmtExpr->getBeginLoc(), false,
8389+
FmtArgs[RefArgs.size()].getSourceRange());
83948390
S.Diag(Ref->getBeginLoc(), diag::note_format_cmp_with) << 1;
8391+
} else if (RefArgs.size() > FmtArgs.size()) {
8392+
CommonArgs = FmtArgs.size();
8393+
// "fewer specifiers in format string than expected"
8394+
CheckFormatHandler::EmitFormatDiagnostic(
8395+
S, InFunctionCall, FmtExpr,
8396+
S.PDiag(diag::warn_format_cmp_specifier_arity) << 0,
8397+
FmtExpr->getBeginLoc(), false, Fmt->getSourceRange());
8398+
S.Diag(Ref->getBeginLoc(), diag::note_format_cmp_with)
8399+
<< 1 << RefArgs[FmtArgs.size()].getSourceRange();
83958400
} else {
83968401
CommonArgs = FmtArgs.size();
83978402
}
@@ -8477,7 +8482,8 @@ static void CheckFormatString(
84778482
DecomposePrintfHandler::GetSpecifiers(S, FExpr, Type, IsObjC,
84788483
inFunctionCall, FmtArgs)) {
84798484
CompareFormatSpecifiers(S, ReferenceFormatString, RefArgs,
8480-
FExpr->getFormatString(), FmtArgs);
8485+
FExpr->getFormatString(), FmtArgs,
8486+
Args[format_idx], inFunctionCall);
84818487
}
84828488
}
84838489
} else if (Type == Sema::FST_Scanf) {

clang/test/Sema/format-string-matches.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,10 @@ void vformat(const char *fmt, ...) {
4141
// MARK: -
4242
// Calling a function with format_matches diagnoses for incompatible formats.
4343

44-
void cvt_percent(const char *c) __attribute__((format_matches(printf, 1, "%%"))); // expected-note{{comparing with this format string}}
44+
void cvt_percent(const char *c) __attribute__((format_matches(printf, 1, "%%"))); // expected-note 2{{comparing with this format string}}
4545
void cvt_at(const char *c) __attribute__((format_matches(NSString, 1, "%@"))); // \
46-
expected-warning{{data argument not used by format string}} \
4746
expected-note{{comparing with this specifier}} \
48-
expected-note{{comparing with this format string}}
47+
expected-note 3{{comparing with this format string}}
4948
void cvt_c(const char *c) __attribute__((format_matches(printf, 1, "%c"))); // expected-note{{comparing with this specifier}}
5049
void cvt_u(const char *c) __attribute__((format_matches(printf, 1, "%u"))); // expected-note{{comparing with this specifier}}
5150
void cvt_hhi(const char *c) __attribute__((format_matches(printf, 1, "%hhi"))); // expected-note 3{{comparing with this specifier}}
@@ -75,12 +74,18 @@ void test_compatibility(void) {
7574
cvt_at("%p"); // expected-warning{{format specifier 'p' is incompatible with '@'}}
7675

7776
cvt_percent("hello");
78-
cvt_percent("%c"); // expected-warning{{fewer specifiers in format string than expected}}
77+
cvt_percent("%c"); // expected-warning{{more specifiers in format string than expected}}
78+
79+
const char *const too_many = "%c"; // expected-note{{format string is defined here}}
80+
cvt_percent(too_many); // expected-warning{{more specifiers in format string than expected}}
7981
}
8082

8183
void test_too_few_args(void) {
82-
cvt_at("a"); // expected-note{{comparing with this format string}}
83-
cvt_at("%@ %@"); // expected-warning{{fewer specifiers in format string than expected}}
84+
cvt_at("a"); // expected-warning{{fewer specifiers in format string than expected}}
85+
cvt_at("%@ %@"); // expected-warning{{more specifiers in format string than expected}}
86+
87+
const char *const too_few = "a"; // expected-note{{format string is defined here}}
88+
cvt_at(too_few); // expected-warning{{fewer specifiers in format string than expected}}
8489
}
8590

8691
void cvt_several(const char *c) __attribute__((format_matches(printf, 1, "%f %i %s"))); // expected-note{{comparing with this specifier}}

0 commit comments

Comments
 (0)