Fixes PR9751. Any other expression kinds we should look through in
NoteCallLocation? I'm a little unhappy with the pile of dyn_cast<>s
stolen from Sema::SemaCheckStringLiteral for handling format_arg
functions, but I don't think it's worth pulling them out into a
helper.
-Matt
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 0b3f0c6..680783b 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -4521,7 +4521,9 @@ def warn_printf_ignored_flag: Warning<
def warn_scanf_scanlist_incomplete : Warning<
"no closing ']' for '%%[' in scanf format string">,
InGroup<Format>;
-
+def note_format_call_location : Note<
+ "format string used here">;
+
// CHECK: returning address/reference of stack memory
def warn_ret_stack_addr : Warning<
"address of stack memory associated with local variable %0 returned">,
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index 74c69a3..dc53508 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -1494,6 +1494,8 @@ protected:
const analyze_format_string::ConversionSpecifier &CS,
const char *startSpecifier, unsigned specifierLen,
unsigned argIndex);
+
+ void NoteCallLocation();
};
}
@@ -1521,6 +1523,7 @@ void CheckFormatHandler::HandleIncompleteSpecifier(const char *startSpecifier,
SourceLocation Loc = getLocationOfByte(startSpecifier);
S.Diag(Loc, diag::warn_printf_incomplete_specifier)
<< getSpecifierRange(startSpecifier, specifierLen);
+ NoteCallLocation();
}
void
@@ -1529,6 +1532,7 @@ CheckFormatHandler::HandleInvalidPosition(const char *startPos, unsigned posLen,
SourceLocation Loc = getLocationOfByte(startPos);
S.Diag(Loc, diag::warn_format_invalid_positional_specifier)
<< (unsigned) p << getSpecifierRange(startPos, posLen);
+ NoteCallLocation();
}
void CheckFormatHandler::HandleZeroPosition(const char *startPos,
@@ -1536,6 +1540,7 @@ void CheckFormatHandler::HandleZeroPosition(const char *startPos,
SourceLocation Loc = getLocationOfByte(startPos);
S.Diag(Loc, diag::warn_format_zero_positional_specifier)
<< getSpecifierRange(startPos, posLen);
+ NoteCallLocation();
}
void CheckFormatHandler::HandleNullChar(const char *nullCharacter) {
@@ -1544,6 +1549,7 @@ void CheckFormatHandler::HandleNullChar(const char *nullCharacter) {
S.Diag(getLocationOfByte(nullCharacter),
diag::warn_printf_format_string_contains_null_char)
<< getFormatStringRange();
+ NoteCallLocation();
}
}
@@ -1593,6 +1599,7 @@ CheckFormatHandler::HandleInvalidConversionSpecifier(unsigned argIndex,
S.Diag(Loc, diag::warn_format_invalid_conversion)
<< StringRef(csStart, csLen)
<< getSpecifierRange(startSpec, specifierLen);
+ NoteCallLocation();
return keepGoing;
}
@@ -1615,12 +1622,40 @@ CheckFormatHandler::CheckNumArgs(
diag::warn_printf_insufficient_data_args)
<< getSpecifierRange(startSpecifier, specifierLen);
}
-
+ NoteCallLocation();
+
return false;
}
return true;
}
+/// If the format string is not a literal within the call (i.e., is a literal
+/// defined elsewhere), emit a note pointing at the callsite.
+void
+CheckFormatHandler::NoteCallLocation() {
+ const Expr* FormatArg = TheCall->getArg(FormatIdx)->IgnoreParenImpCasts();
+ // Look through format_arg functions, e.g. gettext().
+ if (const CallExpr* CE = dyn_cast<CallExpr>(FormatArg)) {
+ if (const ImplicitCastExpr *ICE
+ = dyn_cast<ImplicitCastExpr>(CE->getCallee())) {
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(ICE->getSubExpr())) {
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) {
+ if (const FormatArgAttr *FA = FD->getAttr<FormatArgAttr>()) {
+ FormatArg =
+ CE->getArg(FA->getFormatIdx() - 1)->IgnoreParenImpCasts();
+ }
+ }
+ }
+ }
+ }
+ if (FormatArg == OrigFormatExpr)
+ return;
+ SourceRange FormatArgRange = FormatArg->getSourceRange();
+ S.Diag(FormatArgRange.getBegin(), diag::note_format_call_location)
+ << FormatArgRange;
+}
+
+
//===--- CHECK: Printf format string checking ------------------------------===//
namespace {
@@ -1686,6 +1721,7 @@ bool CheckPrintfHandler::HandleAmount(
S.Diag(getLocationOfByte(Amt.getStart()),
diag::warn_printf_asterisk_missing_arg)
<< k << getSpecifierRange(startSpecifier, specifierLen);
+ NoteCallLocation();
// Don't do any more checking. We will just emit
// spurious errors.
return false;
@@ -1709,6 +1745,7 @@ bool CheckPrintfHandler::HandleAmount(
<< ATR.getRepresentativeType(S.Context) << T
<< getSpecifierRange(startSpecifier, specifierLen)
<< Arg->getSourceRange();
+ NoteCallLocation();
// Don't do any more checking. We will just emit
// spurious errors.
return false;
@@ -1735,6 +1772,7 @@ void CheckPrintfHandler::HandleInvalidAmount(
<< getSpecifierRange(startSpecifier, specifierLen)
<< FixItHint::CreateRemoval(getSpecifierRange(Amt.getStart(),
Amt.getConstantLength()));
+ NoteCallLocation();
break;
default:
@@ -1743,6 +1781,7 @@ void CheckPrintfHandler::HandleInvalidAmount(
<< type
<< CS.toString()
<< getSpecifierRange(startSpecifier, specifierLen);
+ NoteCallLocation();
break;
}
}
@@ -1759,6 +1798,7 @@ void CheckPrintfHandler::HandleFlag(const analyze_printf::PrintfSpecifier &FS,
<< flag.toString() << CS.toString()
<< getSpecifierRange(startSpecifier, specifierLen)
<< FixItHint::CreateRemoval(getSpecifierRange(flag.getPosition(), 1));
+ NoteCallLocation();
}
void CheckPrintfHandler::HandleIgnoredFlag(
@@ -1774,6 +1814,7 @@ void CheckPrintfHandler::HandleIgnoredFlag(
<< getSpecifierRange(startSpecifier, specifierLen)
<< FixItHint::CreateRemoval(getSpecifierRange(
ignoredFlag.getPosition(), 1));
+ NoteCallLocation();
}
bool
@@ -1796,6 +1837,7 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
S.Diag(getLocationOfByte(CS.getStart()),
diag::warn_format_mix_positional_nonpositional_args)
<< getSpecifierRange(startSpecifier, specifierLen);
+ NoteCallLocation();
return false;
}
}
@@ -1870,19 +1912,22 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
// Check the length modifier is valid with the given conversion specifier.
const LengthModifier &LM = FS.getLengthModifier();
- if (!FS.hasValidLengthModifier())
+ if (!FS.hasValidLengthModifier()) {
S.Diag(getLocationOfByte(LM.getStart()),
diag::warn_format_nonsensical_length)
<< LM.toString() << CS.toString()
<< getSpecifierRange(startSpecifier, specifierLen)
<< FixItHint::CreateRemoval(getSpecifierRange(LM.getStart(),
LM.getLength()));
+ NoteCallLocation();
+ }
// Are we using '%n'?
if (CS.getKind() == ConversionSpecifier::nArg) {
// Issue a warning about this being a possible security issue.
S.Diag(getLocationOfByte(CS.getStart()), diag::warn_printf_write_back)
<< getSpecifierRange(startSpecifier, specifierLen);
+ NoteCallLocation();
// Continue checking the other format specifiers.
return true;
}
@@ -1939,6 +1984,7 @@ CheckPrintfHandler::HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier
<< getSpecifierRange(startSpecifier, specifierLen)
<< Ex->getSourceRange();
}
+ NoteCallLocation();
}
return true;
@@ -1975,6 +2021,7 @@ void CheckScanfHandler::HandleIncompleteScanList(const char *start,
const char *end) {
S.Diag(getLocationOfByte(end), diag::warn_scanf_scanlist_incomplete)
<< getSpecifierRange(start, end - start);
+ NoteCallLocation();
}
bool CheckScanfHandler::HandleInvalidScanfConversionSpecifier(
@@ -2013,6 +2060,7 @@ bool CheckScanfHandler::HandleScanfSpecifier(
S.Diag(getLocationOfByte(CS.getStart()),
diag::warn_format_mix_positional_nonpositional_args)
<< getSpecifierRange(startSpecifier, specifierLen);
+ NoteCallLocation();
return false;
}
}
@@ -2026,6 +2074,7 @@ bool CheckScanfHandler::HandleScanfSpecifier(
S.Diag(getLocationOfByte(Amt.getStart()),
diag::warn_scanf_nonzero_width)
<< R << FixItHint::CreateRemoval(R);
+ NoteCallLocation();
}
}
@@ -2053,6 +2102,7 @@ bool CheckScanfHandler::HandleScanfSpecifier(
<< getSpecifierRange(startSpecifier, specifierLen)
<< FixItHint::CreateRemoval(getSpecifierRange(LM.getStart(),
LM.getLength()));
+ NoteCallLocation();
}
// The remaining checks depend on the data arguments.
diff --git a/test/Sema/format-strings.c b/test/Sema/format-strings.c
index 6b5f7e2..59a7632 100644
--- a/test/Sema/format-strings.c
+++ b/test/Sema/format-strings.c
@@ -70,6 +70,8 @@ void check_writeback_specifier()
void check_invalid_specifier(FILE* fp, char *buf)
{
printf("%s%lb%d","unix",10,20); // expected-warning {{invalid conversion specifier 'b'}}
+ const char fmt[] = "%s%lb%d"; // expected-warning {{invalid conversion specifier 'b'}}
+ printf(fmt,"unix",10,20); // expected-note {{format string used here}}
fprintf(fp,"%%%l"); // expected-warning {{incomplete format specifier}}
sprintf(buf,"%%%%%ld%d%d", 1, 2, 3); // expected-warning{{conversion specifies type 'long' but the argument has type 'int'}}
snprintf(buf, 2, "%%%%%ld%;%d", 1, 2, 3); // expected-warning{{conversion specifies type 'long' but the argument has type 'int'}} expected-warning {{invalid conversion specifier ';'}}
@@ -151,6 +153,8 @@ void torture(va_list v8) {
void test10(int x, float f, int i, long long lli) {
printf("%s"); // expected-warning{{more '%' conversions than data arguments}}
+ const char fmt[] = "%s"; // expected-warning{{more '%' conversions than data arguments}}
+ printf(fmt); // expected-note{{format string used here}}
printf("%@", 12); // expected-warning{{invalid conversion specifier '@'}}
printf("\0"); // expected-warning{{format string contains '\0' within the string body}}
printf("xs\0"); // expected-warning{{format string contains '\0' within the string body}}
@@ -386,4 +390,3 @@ void test_suppress_invalid_specifier() {
printf("%@", 12); // no-warning
#pragma clang diagnostic pop
}
-
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits