aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Wennborg <hans@hanshq.net>2011-12-15 10:25:47 +0000
committerHans Wennborg <hans@hanshq.net>2011-12-15 10:25:47 +0000
commitd02deebce5f1b283101e035a7f5d5bab0d2068ec (patch)
tree58459abff0fef9bd9fe64d4188a7c702b6e06cdf
parentdb7a800e0b76036d0faa7123f2e05e45ee3294e5 (diff)
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). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@146649 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Analysis/Analyses/FormatString.h5
-rw-r--r--lib/Analysis/FormatString.cpp29
-rw-r--r--lib/Analysis/FormatStringParsing.h6
-rw-r--r--lib/Analysis/PrintfFormatString.cpp15
-rw-r--r--lib/Analysis/ScanfFormatString.cpp17
-rw-r--r--lib/Sema/SemaChecking.cpp6
-rw-r--r--test/Sema/format-strings-c90.c21
-rw-r--r--test/Sema/format-strings-scanf.c9
8 files changed, 92 insertions, 16 deletions
diff --git a/include/clang/Analysis/Analyses/FormatString.h b/include/clang/Analysis/Analyses/FormatString.h
index f84ca988ec..7e34277ad0 100644
--- a/include/clang/Analysis/Analyses/FormatString.h
+++ b/include/clang/Analysis/Analyses/FormatString.h
@@ -71,6 +71,7 @@ public:
AsSizeT, // 'z'
AsPtrDiff, // 't'
AsLongDouble, // 'L'
+ AsAllocate, // for '%as', GNU extension to C90 scanf
AsWideChar = AsLong // for '%ls', only makes sense for printf
};
@@ -630,10 +631,10 @@ public:
};
bool ParsePrintfString(FormatStringHandler &H,
- const char *beg, const char *end);
+ const char *beg, const char *end, const LangOptions &LO);
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 clang namespace
diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp
index 2d56c233b6..bbc6f07b36 100644
--- a/lib/Analysis/FormatString.cpp
+++ b/lib/Analysis/FormatString.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "FormatStringParsing.h"
+#include "clang/Basic/LangOptions.h"
using clang::analyze_format_string::ArgTypeResult;
using clang::analyze_format_string::FormatStringHandler;
@@ -175,7 +176,9 @@ clang::analyze_format_string::ParseArgPosition(FormatStringHandler &H,
bool
clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS,
const char *&I,
- const char *E) {
+ const char *E,
+ const LangOptions &LO,
+ bool IsScanf) {
LengthModifier::Kind lmKind = LengthModifier::None;
const char *lmPosition = I;
switch (*I) {
@@ -196,6 +199,19 @@ clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS,
case 't': lmKind = LengthModifier::AsPtrDiff; ++I; break;
case 'L': lmKind = LengthModifier::AsLongDouble; ++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);
FS.setLengthModifier(lm);
@@ -391,6 +407,8 @@ analyze_format_string::LengthModifier::toString() const {
return "t";
case AsLongDouble:
return "L";
+ case AsAllocate:
+ return "a";
case None:
return "";
}
@@ -527,6 +545,15 @@ bool FormatSpecifier::hasValidLengthModifier() const {
default:
return false;
}
+
+ case LengthModifier::AsAllocate:
+ switch (CS.getKind()) {
+ case ConversionSpecifier::sArg:
+ case ConversionSpecifier::SArg:
+ return true;
+ default:
+ return false;
+ }
}
return false;
}
diff --git a/lib/Analysis/FormatStringParsing.h b/lib/Analysis/FormatStringParsing.h
index 607e99ccd0..f483ec6fac 100644
--- a/lib/Analysis/FormatStringParsing.h
+++ b/lib/Analysis/FormatStringParsing.h
@@ -8,6 +8,8 @@
namespace clang {
+class LangOptions;
+
template <typename T>
class UpdateOnReturn {
T &ValueToUpdate;
@@ -42,7 +44,8 @@ bool ParseArgPosition(FormatStringHandler &H,
/// Returns true if a LengthModifier was parsed and installed in the
/// 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 {
T FS;
@@ -69,4 +72,3 @@ public:
} // end clang namespace
#endif
-
diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp
index 45e32681c1..4e9a4641d7 100644
--- a/lib/Analysis/PrintfFormatString.cpp
+++ b/lib/Analysis/PrintfFormatString.cpp
@@ -51,7 +51,8 @@ static bool ParsePrecision(FormatStringHandler &H, PrintfSpecifier &FS,
static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
const char *&Beg,
const char *E,
- unsigned &argIndex) {
+ unsigned &argIndex,
+ const LangOptions &LO) {
using namespace clang::analyze_format_string;
using namespace clang::analyze_printf;
@@ -150,7 +151,7 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
}
// Look for the length modifier.
- if (ParseLengthModifier(FS, I, E) && I == E) {
+ if (ParseLengthModifier(FS, I, E, LO) && I == E) {
// No more characters left?
H.HandleIncompleteSpecifier(Start, E - Start);
return true;
@@ -210,13 +211,15 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H,
const char *I,
- const char *E) {
+ const char *E,
+ const LangOptions &LO) {
unsigned argIndex = 0;
// Keep looking for a format specifier until we have exhausted the string.
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?
// If so, don't do any more processing.
if (FSR.shouldStop())
@@ -269,6 +272,8 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx) const {
return ArgTypeResult();
case LengthModifier::AsPtrDiff:
return ArgTypeResult(Ctx.getPointerDiffType(), "ptrdiff_t");
+ case LengthModifier::AsAllocate:
+ return ArgTypeResult::Invalid();
}
if (CS.isUIntArg())
@@ -288,6 +293,8 @@ ArgTypeResult PrintfSpecifier::getArgType(ASTContext &Ctx) const {
// FIXME: How to get the corresponding unsigned
// version of ptrdiff_t?
return ArgTypeResult();
+ case LengthModifier::AsAllocate:
+ return ArgTypeResult::Invalid();
}
if (CS.isDoubleArg()) {
diff --git a/lib/Analysis/ScanfFormatString.cpp b/lib/Analysis/ScanfFormatString.cpp
index 77d9c9658d..e8bd0d0e24 100644
--- a/lib/Analysis/ScanfFormatString.cpp
+++ b/lib/Analysis/ScanfFormatString.cpp
@@ -67,7 +67,8 @@ static bool ParseScanList(FormatStringHandler &H,
static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H,
const char *&Beg,
const char *E,
- unsigned &argIndex) {
+ unsigned &argIndex,
+ const LangOptions &LO) {
using namespace clang::analyze_scanf;
const char *I = Beg;
@@ -132,7 +133,7 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H,
}
// 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?
H.HandleIncompleteSpecifier(Start, E - Start);
return true;
@@ -218,6 +219,7 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const {
case LengthModifier::AsPtrDiff:
return ScanfArgTypeResult(Ctx.getPointerDiffType(), "ptrdiff_t *");
case LengthModifier::AsLongDouble: return ScanfArgTypeResult::Invalid();
+ case LengthModifier::AsAllocate: return ScanfArgTypeResult::Invalid();
}
// Unsigned int.
@@ -240,6 +242,7 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const {
// FIXME: Unsigned version of ptrdiff_t?
return ScanfArgTypeResult();
case LengthModifier::AsLongDouble: return ScanfArgTypeResult::Invalid();
+ case LengthModifier::AsAllocate: return ScanfArgTypeResult::Invalid();
}
// Float.
@@ -274,7 +277,9 @@ ScanfArgTypeResult ScanfSpecifier::getArgType(ASTContext &Ctx) const {
case ConversionSpecifier::CArg:
case ConversionSpecifier::SArg:
// 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.
case ConversionSpecifier::pArg:
@@ -401,13 +406,15 @@ void ScanfSpecifier::toString(raw_ostream &os) const {
bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H,
const char *I,
- const char *E) {
+ const char *E,
+ const LangOptions &LO) {
unsigned argIndex = 0;
// Keep looking for a format specifier until we have exhausted the string.
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?
// If so, don't do any more processing.
if (FSR.shouldStop())
diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp
index ebdc8321f7..c8f2d05e0d 100644
--- a/lib/Sema/SemaChecking.cpp
+++ b/lib/Sema/SemaChecking.cpp
@@ -2442,7 +2442,8 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
Str, HasVAListArg, TheCall, format_idx,
inFunctionCall);
- if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen))
+ if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen,
+ getLangOptions()))
H.DoneProcessing();
}
else {
@@ -2451,7 +2452,8 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
Str, HasVAListArg, TheCall, format_idx,
inFunctionCall);
- if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen))
+ if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen,
+ getLangOptions()))
H.DoneProcessing();
}
}
diff --git a/test/Sema/format-strings-c90.c b/test/Sema/format-strings-c90.c
new file mode 100644
index 0000000000..1b00c38c04
--- /dev/null
+++ b/test/Sema/format-strings-c90.c
@@ -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);
+}
diff --git a/test/Sema/format-strings-scanf.c b/test/Sema/format-strings-scanf.c
index 6abd0687ef..b20e1b00f4 100644
--- a/test/Sema/format-strings-scanf.c
+++ b/test/Sema/format-strings-scanf.c
@@ -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}}
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 **'}}
+}