Support the 'a' length modifier in scanf format strings as a C90

extension.

This fixes gcc.dg/format/c90-scanf-3.c and ext-4.c (test for excess
errors).

llvm-svn: 146649
This commit is contained in:
Hans Wennborg 2011-12-15 10:25:47 +00:00
parent adba86d63c
commit 23926bd2d7
8 changed files with 92 additions and 16 deletions

View File

@ -71,6 +71,7 @@ public:
AsSizeT, // 'z' AsSizeT, // 'z'
AsPtrDiff, // 't' AsPtrDiff, // 't'
AsLongDouble, // 'L' AsLongDouble, // 'L'
AsAllocate, // for '%as', GNU extension to C90 scanf
AsWideChar = AsLong // for '%ls', only makes sense for printf AsWideChar = AsLong // for '%ls', only makes sense for printf
}; };
@ -630,10 +631,10 @@ public:
}; };
bool ParsePrintfString(FormatStringHandler &H, bool ParsePrintfString(FormatStringHandler &H,
const char *beg, const char *end); const char *beg, const char *end, const LangOptions &LO);
bool ParseScanfString(FormatStringHandler &H, bool ParseScanfString(FormatStringHandler &H,
const char *beg, const char *end); const char *beg, const char *end, const LangOptions &LO);
} // end analyze_format_string namespace } // end analyze_format_string namespace
} // end clang namespace } // end clang namespace

View File

@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "FormatStringParsing.h" #include "FormatStringParsing.h"
#include "clang/Basic/LangOptions.h"
using clang::analyze_format_string::ArgTypeResult; using clang::analyze_format_string::ArgTypeResult;
using clang::analyze_format_string::FormatStringHandler; using clang::analyze_format_string::FormatStringHandler;
@ -175,7 +176,9 @@ clang::analyze_format_string::ParseArgPosition(FormatStringHandler &H,
bool bool
clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS,
const char *&I, const char *&I,
const char *E) { const char *E,
const LangOptions &LO,
bool IsScanf) {
LengthModifier::Kind lmKind = LengthModifier::None; LengthModifier::Kind lmKind = LengthModifier::None;
const char *lmPosition = I; const char *lmPosition = I;
switch (*I) { switch (*I) {
@ -196,6 +199,19 @@ clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS,
case 't': lmKind = LengthModifier::AsPtrDiff; ++I; break; case 't': lmKind = LengthModifier::AsPtrDiff; ++I; break;
case 'L': lmKind = LengthModifier::AsLongDouble; ++I; break; case 'L': lmKind = LengthModifier::AsLongDouble; ++I; break;
case 'q': lmKind = LengthModifier::AsLongLong; ++I; break; case 'q': lmKind = LengthModifier::AsLongLong; ++I; break;
case 'a':
if (IsScanf && !LO.C99 && !LO.CPlusPlus) {
// For scanf in C90, look at the next character to see if this should
// be parsed as the GNU extension 'a' length modifier. If not, this
// will be parsed as a conversion specifier.
++I;
if (I != E && (*I == 's' || *I == 'S' || *I == '[')) {
lmKind = LengthModifier::AsAllocate;
break;
}
--I;
}
return false;
} }
LengthModifier lm(lmPosition, lmKind); LengthModifier lm(lmPosition, lmKind);
FS.setLengthModifier(lm); FS.setLengthModifier(lm);
@ -391,6 +407,8 @@ analyze_format_string::LengthModifier::toString() const {
return "t"; return "t";
case AsLongDouble: case AsLongDouble:
return "L"; return "L";
case AsAllocate:
return "a";
case None: case None:
return ""; return "";
} }
@ -527,6 +545,15 @@ bool FormatSpecifier::hasValidLengthModifier() const {
default: default:
return false; return false;
} }
case LengthModifier::AsAllocate:
switch (CS.getKind()) {
case ConversionSpecifier::sArg:
case ConversionSpecifier::SArg:
return true;
default:
return false;
}
} }
return false; return false;
} }

View File

@ -8,6 +8,8 @@
namespace clang { namespace clang {
class LangOptions;
template <typename T> template <typename T>
class UpdateOnReturn { class UpdateOnReturn {
T &ValueToUpdate; T &ValueToUpdate;
@ -42,7 +44,8 @@ bool ParseArgPosition(FormatStringHandler &H,
/// Returns true if a LengthModifier was parsed and installed in the /// Returns true if a LengthModifier was parsed and installed in the
/// FormatSpecifier& argument, and false otherwise. /// FormatSpecifier& argument, and false otherwise.
bool ParseLengthModifier(FormatSpecifier &FS, const char *&Beg, const char *E); bool ParseLengthModifier(FormatSpecifier &FS, const char *&Beg, const char *E,
const LangOptions &LO, bool IsScanf = false);
template <typename T> class SpecifierResult { template <typename T> class SpecifierResult {
T FS; T FS;
@ -69,4 +72,3 @@ public:
} // end clang namespace } // end clang namespace
#endif #endif

View File

@ -51,7 +51,8 @@ static bool ParsePrecision(FormatStringHandler &H, PrintfSpecifier &FS,
static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
const char *&Beg, const char *&Beg,
const char *E, const char *E,
unsigned &argIndex) { unsigned &argIndex,
const LangOptions &LO) {
using namespace clang::analyze_format_string; using namespace clang::analyze_format_string;
using namespace clang::analyze_printf; using namespace clang::analyze_printf;
@ -150,7 +151,7 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
} }
// Look for the length modifier. // Look for the length modifier.
if (ParseLengthModifier(FS, I, E) && I == E) { if (ParseLengthModifier(FS, I, E, LO) && I == E) {
// No more characters left? // No more characters left?
H.HandleIncompleteSpecifier(Start, E - Start); H.HandleIncompleteSpecifier(Start, E - Start);
return true; return true;
@ -210,13 +211,15 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H, bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H,
const char *I, const char *I,
const char *E) { const char *E,
const LangOptions &LO) {
unsigned argIndex = 0; unsigned argIndex = 0;
// Keep looking for a format specifier until we have exhausted the string. // Keep looking for a format specifier until we have exhausted the string.
while (I != E) { while (I != E) {
const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex); const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex,
LO);
// Did a fail-stop error of any kind occur when parsing the specifier? // Did a fail-stop error of any kind occur when parsing the specifier?
// If so, don't do any more processing. // If so, don't do any more processing.
if (FSR.shouldStop()) if (FSR.shouldStop())
@ -269,6 +272,8 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx) const {
return ArgTypeResult(); return ArgTypeResult();
case LengthModifier::AsPtrDiff: case LengthModifier::AsPtrDiff:
return ArgTypeResult(Ctx.getPointerDiffType(), "ptrdiff_t"); return ArgTypeResult(Ctx.getPointerDiffType(), "ptrdiff_t");
case LengthModifier::AsAllocate:
return ArgTypeResult::Invalid();
} }
if (CS.isUIntArg()) if (CS.isUIntArg())
@ -288,6 +293,8 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx) const {
// FIXME: How to get the corresponding unsigned // FIXME: How to get the corresponding unsigned
// version of ptrdiff_t? // version of ptrdiff_t?
return ArgTypeResult(); return ArgTypeResult();
case LengthModifier::AsAllocate:
return ArgTypeResult::Invalid();
} }
if (CS.isDoubleArg()) { if (CS.isDoubleArg()) {

View File

@ -67,7 +67,8 @@ static bool ParseScanList(FormatStringHandler &H,
static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H, static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H,
const char *&Beg, const char *&Beg,
const char *E, const char *E,
unsigned &argIndex) { unsigned &argIndex,
const LangOptions &LO) {
using namespace clang::analyze_scanf; using namespace clang::analyze_scanf;
const char *I = Beg; const char *I = Beg;
@ -132,7 +133,7 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H,
} }
// Look for the length modifier. // Look for the length modifier.
if (ParseLengthModifier(FS, I, E) && I == E) { if (ParseLengthModifier(FS, I, E, LO, /*scanf=*/true) && I == E) {
// No more characters left? // No more characters left?
H.HandleIncompleteSpecifier(Start, E - Start); H.HandleIncompleteSpecifier(Start, E - Start);
return true; return true;
@ -218,6 +219,7 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const {
case LengthModifier::AsPtrDiff: case LengthModifier::AsPtrDiff:
return ScanfArgTypeResult(Ctx.getPointerDiffType(), "ptrdiff_t *"); return ScanfArgTypeResult(Ctx.getPointerDiffType(), "ptrdiff_t *");
case LengthModifier::AsLongDouble: return ScanfArgTypeResult::Invalid(); case LengthModifier::AsLongDouble: return ScanfArgTypeResult::Invalid();
case LengthModifier::AsAllocate: return ScanfArgTypeResult::Invalid();
} }
// Unsigned int. // Unsigned int.
@ -240,6 +242,7 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const {
// FIXME: Unsigned version of ptrdiff_t? // FIXME: Unsigned version of ptrdiff_t?
return ScanfArgTypeResult(); return ScanfArgTypeResult();
case LengthModifier::AsLongDouble: return ScanfArgTypeResult::Invalid(); case LengthModifier::AsLongDouble: return ScanfArgTypeResult::Invalid();
case LengthModifier::AsAllocate: return ScanfArgTypeResult::Invalid();
} }
// Float. // Float.
@ -274,7 +277,9 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const {
case ConversionSpecifier::CArg: case ConversionSpecifier::CArg:
case ConversionSpecifier::SArg: case ConversionSpecifier::SArg:
// FIXME: Mac OS X specific? // FIXME: Mac OS X specific?
return ScanfArgTypeResult(ScanfArgTypeResult::WCStrTy, "wchar_t *"); if (LM.getKind() == LengthModifier::None)
return ScanfArgTypeResult(ScanfArgTypeResult::WCStrTy, "wchar_t *");
return ScanfArgTypeResult::Invalid();
// Pointer. // Pointer.
case ConversionSpecifier::pArg: case ConversionSpecifier::pArg:
@ -401,13 +406,15 @@ void ScanfSpecifier::toString(raw_ostream &os) const {
bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H, bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H,
const char *I, const char *I,
const char *E) { const char *E,
const LangOptions &LO) {
unsigned argIndex = 0; unsigned argIndex = 0;
// Keep looking for a format specifier until we have exhausted the string. // Keep looking for a format specifier until we have exhausted the string.
while (I != E) { while (I != E) {
const ScanfSpecifierResult &FSR = ParseScanfSpecifier(H, I, E, argIndex); const ScanfSpecifierResult &FSR = ParseScanfSpecifier(H, I, E, argIndex,
LO);
// Did a fail-stop error of any kind occur when parsing the specifier? // Did a fail-stop error of any kind occur when parsing the specifier?
// If so, don't do any more processing. // If so, don't do any more processing.
if (FSR.shouldStop()) if (FSR.shouldStop())

View File

@ -2442,7 +2442,8 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
Str, HasVAListArg, TheCall, format_idx, Str, HasVAListArg, TheCall, format_idx,
inFunctionCall); inFunctionCall);
if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen)) if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen,
getLangOptions()))
H.DoneProcessing(); H.DoneProcessing();
} }
else { else {
@ -2451,7 +2452,8 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
Str, HasVAListArg, TheCall, format_idx, Str, HasVAListArg, TheCall, format_idx,
inFunctionCall); inFunctionCall);
if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen)) if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen,
getLangOptions()))
H.DoneProcessing(); H.DoneProcessing();
} }
} }

View File

@ -0,0 +1,21 @@
/* RUN: %clang_cc1 -fsyntax-only -verify -pedantic -std=c89 %s
*/
int scanf(const char * restrict, ...);
int printf(const char *restrict, ...);
void foo(char **sp, float *fp, int *ip) {
/* TODO: Warn that the 'a' length modifier is an extension. */
scanf("%as", sp);
/* TODO: Warn that the 'a' conversion specifier is a C99 feature. */
scanf("%a", fp);
scanf("%afoobar", fp);
printf("%a", 1.0);
printf("%as", 1.0);
printf("%aS", 1.0);
printf("%a[", 1.0);
printf("%afoo", 1.0);
scanf("%da", ip);
}

View File

@ -67,3 +67,12 @@ void test_variants(int *i, const char *s, ...) {
vfscanf(f, "%[abc", ap); // expected-warning{{no closing ']' for '%[' in scanf format string}} vfscanf(f, "%[abc", ap); // expected-warning{{no closing ']' for '%[' in scanf format string}}
vsscanf(buf, "%[abc", ap); // expected-warning{{no closing ']' for '%[' in scanf format string}} vsscanf(buf, "%[abc", ap); // expected-warning{{no closing ']' for '%[' in scanf format string}}
} }
void test_alloc_extension(char **sp, wchar_t **lsp) {
/* Make sure "%a" gets parsed as a conversion specifier for float,
* even when followed by an 's', 'S' or '[', which would cause it to be
* parsed as a length modifier in C90. */
scanf("%as", sp); // expected-warning{{conversion specifies type 'float *' but the argument has type 'char **'}}
scanf("%aS", lsp); // expected-warning{{conversion specifies type 'float *' but the argument has type 'wchar_t **' (aka 'int **')}}
scanf("%a[bcd]", sp); // expected-warning{{conversion specifies type 'float *' but the argument has type 'char **'}}
}