forked from OSchip/llvm-project
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:
parent
adba86d63c
commit
23926bd2d7
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 **'}}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue