forked from OSchip/llvm-project
Format strings: %D, %U, and %O are valid on Darwin (same as %d, %u, %o).
These will warn under -Wformat-non-iso, and will still be rejected outright on other platforms. <rdar://problem/12061922> llvm-svn: 163771
This commit is contained in:
parent
6a465cd8ef
commit
510260c2bf
|
@ -117,11 +117,14 @@ public:
|
|||
// C99 conversion specifiers.
|
||||
cArg,
|
||||
dArg,
|
||||
DArg, // Apple extension
|
||||
iArg,
|
||||
IntArgBeg = dArg, IntArgEnd = iArg,
|
||||
|
||||
oArg,
|
||||
OArg, // Apple extension
|
||||
uArg,
|
||||
UArg, // Apple extension
|
||||
xArg,
|
||||
XArg,
|
||||
UIntArgBeg = oArg, UIntArgEnd = XArg,
|
||||
|
@ -628,10 +631,12 @@ public:
|
|||
};
|
||||
|
||||
bool ParsePrintfString(FormatStringHandler &H,
|
||||
const char *beg, const char *end, const LangOptions &LO);
|
||||
const char *beg, const char *end, const LangOptions &LO,
|
||||
const TargetInfo &Target);
|
||||
|
||||
bool ParseScanfString(FormatStringHandler &H,
|
||||
const char *beg, const char *end, const LangOptions &LO);
|
||||
const char *beg, const char *end, const LangOptions &LO,
|
||||
const TargetInfo &Target);
|
||||
|
||||
} // end analyze_format_string namespace
|
||||
} // end clang namespace
|
||||
|
|
|
@ -490,9 +490,12 @@ analyze_format_string::LengthModifier::toString() const {
|
|||
const char *ConversionSpecifier::toString() const {
|
||||
switch (kind) {
|
||||
case dArg: return "d";
|
||||
case DArg: return "D";
|
||||
case iArg: return "i";
|
||||
case oArg: return "o";
|
||||
case OArg: return "O";
|
||||
case uArg: return "u";
|
||||
case UArg: return "U";
|
||||
case xArg: return "x";
|
||||
case XArg: return "X";
|
||||
case fArg: return "f";
|
||||
|
@ -564,9 +567,12 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const {
|
|||
case LengthModifier::AsPtrDiff:
|
||||
switch (CS.getKind()) {
|
||||
case ConversionSpecifier::dArg:
|
||||
case ConversionSpecifier::DArg:
|
||||
case ConversionSpecifier::iArg:
|
||||
case ConversionSpecifier::oArg:
|
||||
case ConversionSpecifier::OArg:
|
||||
case ConversionSpecifier::uArg:
|
||||
case ConversionSpecifier::UArg:
|
||||
case ConversionSpecifier::xArg:
|
||||
case ConversionSpecifier::XArg:
|
||||
case ConversionSpecifier::nArg:
|
||||
|
@ -579,9 +585,12 @@ bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target) const {
|
|||
case LengthModifier::AsLong:
|
||||
switch (CS.getKind()) {
|
||||
case ConversionSpecifier::dArg:
|
||||
case ConversionSpecifier::DArg:
|
||||
case ConversionSpecifier::iArg:
|
||||
case ConversionSpecifier::oArg:
|
||||
case ConversionSpecifier::OArg:
|
||||
case ConversionSpecifier::uArg:
|
||||
case ConversionSpecifier::UArg:
|
||||
case ConversionSpecifier::xArg:
|
||||
case ConversionSpecifier::XArg:
|
||||
case ConversionSpecifier::aArg:
|
||||
|
@ -699,6 +708,9 @@ bool FormatSpecifier::hasStandardConversionSpecifier(const LangOptions &LangOpt)
|
|||
return LangOpt.ObjC1 || LangOpt.ObjC2;
|
||||
case ConversionSpecifier::InvalidSpecifier:
|
||||
case ConversionSpecifier::PrintErrno:
|
||||
case ConversionSpecifier::DArg:
|
||||
case ConversionSpecifier::OArg:
|
||||
case ConversionSpecifier::UArg:
|
||||
return false;
|
||||
}
|
||||
llvm_unreachable("Invalid ConversionSpecifier Kind!");
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Analysis/Analyses/FormatString.h"
|
||||
#include "clang/Basic/TargetInfo.h"
|
||||
#include "FormatStringParsing.h"
|
||||
|
||||
using clang::analyze_format_string::ArgType;
|
||||
|
@ -52,7 +53,8 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
|
|||
const char *&Beg,
|
||||
const char *E,
|
||||
unsigned &argIndex,
|
||||
const LangOptions &LO) {
|
||||
const LangOptions &LO,
|
||||
const TargetInfo &Target) {
|
||||
|
||||
using namespace clang::analyze_format_string;
|
||||
using namespace clang::analyze_printf;
|
||||
|
@ -196,6 +198,19 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
|
|||
case '@': k = ConversionSpecifier::ObjCObjArg; break;
|
||||
// Glibc specific.
|
||||
case 'm': k = ConversionSpecifier::PrintErrno; break;
|
||||
// Apple-specific
|
||||
case 'D':
|
||||
if (Target.getTriple().isOSDarwin())
|
||||
k = ConversionSpecifier::DArg;
|
||||
break;
|
||||
case 'O':
|
||||
if (Target.getTriple().isOSDarwin())
|
||||
k = ConversionSpecifier::OArg;
|
||||
break;
|
||||
case 'U':
|
||||
if (Target.getTriple().isOSDarwin())
|
||||
k = ConversionSpecifier::UArg;
|
||||
break;
|
||||
}
|
||||
PrintfConversionSpecifier CS(conversionPosition, k);
|
||||
FS.setConversionSpecifier(CS);
|
||||
|
@ -212,14 +227,15 @@ static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
|
|||
bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H,
|
||||
const char *I,
|
||||
const char *E,
|
||||
const LangOptions &LO) {
|
||||
const LangOptions &LO,
|
||||
const TargetInfo &Target) {
|
||||
|
||||
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,
|
||||
LO);
|
||||
LO, Target);
|
||||
// Did a fail-stop error of any kind occur when parsing the specifier?
|
||||
// If so, don't do any more processing.
|
||||
if (FSR.shouldStop())
|
||||
|
@ -551,6 +567,7 @@ bool PrintfSpecifier::hasValidPlusPrefix() const {
|
|||
// The plus prefix only makes sense for signed conversions
|
||||
switch (CS.getKind()) {
|
||||
case ConversionSpecifier::dArg:
|
||||
case ConversionSpecifier::DArg:
|
||||
case ConversionSpecifier::iArg:
|
||||
case ConversionSpecifier::fArg:
|
||||
case ConversionSpecifier::FArg:
|
||||
|
@ -574,6 +591,7 @@ bool PrintfSpecifier::hasValidAlternativeForm() const {
|
|||
// Alternate form flag only valid with the oxXaAeEfFgG conversions
|
||||
switch (CS.getKind()) {
|
||||
case ConversionSpecifier::oArg:
|
||||
case ConversionSpecifier::OArg:
|
||||
case ConversionSpecifier::xArg:
|
||||
case ConversionSpecifier::XArg:
|
||||
case ConversionSpecifier::aArg:
|
||||
|
@ -598,9 +616,12 @@ bool PrintfSpecifier::hasValidLeadingZeros() const {
|
|||
// Leading zeroes flag only valid with the diouxXaAeEfFgG conversions
|
||||
switch (CS.getKind()) {
|
||||
case ConversionSpecifier::dArg:
|
||||
case ConversionSpecifier::DArg:
|
||||
case ConversionSpecifier::iArg:
|
||||
case ConversionSpecifier::oArg:
|
||||
case ConversionSpecifier::OArg:
|
||||
case ConversionSpecifier::uArg:
|
||||
case ConversionSpecifier::UArg:
|
||||
case ConversionSpecifier::xArg:
|
||||
case ConversionSpecifier::XArg:
|
||||
case ConversionSpecifier::aArg:
|
||||
|
@ -625,6 +646,7 @@ bool PrintfSpecifier::hasValidSpacePrefix() const {
|
|||
// The space prefix only makes sense for signed conversions
|
||||
switch (CS.getKind()) {
|
||||
case ConversionSpecifier::dArg:
|
||||
case ConversionSpecifier::DArg:
|
||||
case ConversionSpecifier::iArg:
|
||||
case ConversionSpecifier::fArg:
|
||||
case ConversionSpecifier::FArg:
|
||||
|
@ -661,8 +683,10 @@ bool PrintfSpecifier::hasValidThousandsGroupingPrefix() const {
|
|||
|
||||
switch (CS.getKind()) {
|
||||
case ConversionSpecifier::dArg:
|
||||
case ConversionSpecifier::DArg:
|
||||
case ConversionSpecifier::iArg:
|
||||
case ConversionSpecifier::uArg:
|
||||
case ConversionSpecifier::UArg:
|
||||
case ConversionSpecifier::fArg:
|
||||
case ConversionSpecifier::FArg:
|
||||
case ConversionSpecifier::gArg:
|
||||
|
@ -680,9 +704,12 @@ bool PrintfSpecifier::hasValidPrecision() const {
|
|||
// Precision is only valid with the diouxXaAeEfFgGs conversions
|
||||
switch (CS.getKind()) {
|
||||
case ConversionSpecifier::dArg:
|
||||
case ConversionSpecifier::DArg:
|
||||
case ConversionSpecifier::iArg:
|
||||
case ConversionSpecifier::oArg:
|
||||
case ConversionSpecifier::OArg:
|
||||
case ConversionSpecifier::uArg:
|
||||
case ConversionSpecifier::UArg:
|
||||
case ConversionSpecifier::xArg:
|
||||
case ConversionSpecifier::XArg:
|
||||
case ConversionSpecifier::aArg:
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "clang/Analysis/Analyses/FormatString.h"
|
||||
#include "clang/Basic/TargetInfo.h"
|
||||
#include "FormatStringParsing.h"
|
||||
|
||||
using clang::analyze_format_string::ArgType;
|
||||
|
@ -67,7 +68,8 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H,
|
|||
const char *&Beg,
|
||||
const char *E,
|
||||
unsigned &argIndex,
|
||||
const LangOptions &LO) {
|
||||
const LangOptions &LO,
|
||||
const TargetInfo &Target) {
|
||||
|
||||
using namespace clang::analyze_scanf;
|
||||
const char *I = Beg;
|
||||
|
@ -172,6 +174,20 @@ static ScanfSpecifierResult ParseScanfSpecifier(FormatStringHandler &H,
|
|||
case 'o': k = ConversionSpecifier::oArg; break;
|
||||
case 's': k = ConversionSpecifier::sArg; break;
|
||||
case 'p': k = ConversionSpecifier::pArg; break;
|
||||
// Apple extensions
|
||||
// Apple-specific
|
||||
case 'D':
|
||||
if (Target.getTriple().isOSDarwin())
|
||||
k = ConversionSpecifier::DArg;
|
||||
break;
|
||||
case 'O':
|
||||
if (Target.getTriple().isOSDarwin())
|
||||
k = ConversionSpecifier::OArg;
|
||||
break;
|
||||
case 'U':
|
||||
if (Target.getTriple().isOSDarwin())
|
||||
k = ConversionSpecifier::UArg;
|
||||
break;
|
||||
}
|
||||
ScanfConversionSpecifier CS(conversionPosition, k);
|
||||
if (k == ScanfConversionSpecifier::ScanListArg) {
|
||||
|
@ -202,6 +218,7 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
|
|||
switch(CS.getKind()) {
|
||||
// Signed int.
|
||||
case ConversionSpecifier::dArg:
|
||||
case ConversionSpecifier::DArg:
|
||||
case ConversionSpecifier::iArg:
|
||||
switch (LM.getKind()) {
|
||||
case LengthModifier::None:
|
||||
|
@ -233,7 +250,9 @@ ArgType ScanfSpecifier::getArgType(ASTContext &Ctx) const {
|
|||
|
||||
// Unsigned int.
|
||||
case ConversionSpecifier::oArg:
|
||||
case ConversionSpecifier::OArg:
|
||||
case ConversionSpecifier::uArg:
|
||||
case ConversionSpecifier::UArg:
|
||||
case ConversionSpecifier::xArg:
|
||||
case ConversionSpecifier::XArg:
|
||||
switch (LM.getKind()) {
|
||||
|
@ -465,14 +484,15 @@ void ScanfSpecifier::toString(raw_ostream &os) const {
|
|||
bool clang::analyze_format_string::ParseScanfString(FormatStringHandler &H,
|
||||
const char *I,
|
||||
const char *E,
|
||||
const LangOptions &LO) {
|
||||
const LangOptions &LO,
|
||||
const TargetInfo &Target) {
|
||||
|
||||
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,
|
||||
LO);
|
||||
LO, Target);
|
||||
// Did a fail-stop error of any kind occur when parsing the specifier?
|
||||
// If so, don't do any more processing.
|
||||
if (FSR.shouldStop())
|
||||
|
|
|
@ -3020,7 +3020,8 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
|
|||
inFunctionCall, CallType);
|
||||
|
||||
if (!analyze_format_string::ParsePrintfString(H, Str, Str + StrLen,
|
||||
getLangOpts()))
|
||||
getLangOpts(),
|
||||
Context.getTargetInfo()))
|
||||
H.DoneProcessing();
|
||||
} else if (Type == FST_Scanf) {
|
||||
CheckScanfHandler H(*this, FExpr, OrigFormatExpr, firstDataArg, numDataArgs,
|
||||
|
@ -3028,7 +3029,8 @@ void Sema::CheckFormatString(const StringLiteral *FExpr,
|
|||
inFunctionCall, CallType);
|
||||
|
||||
if (!analyze_format_string::ParseScanfString(H, Str, Str + StrLen,
|
||||
getLangOpts()))
|
||||
getLangOpts(),
|
||||
Context.getTargetInfo()))
|
||||
H.DoneProcessing();
|
||||
} // TODO: handle other formats
|
||||
}
|
||||
|
|
|
@ -179,3 +179,13 @@ void testCasts() {
|
|||
// CHECK: fix-it:"{{.*}}":{168:11-168:13}:"%u"
|
||||
// CHECK: fix-it:"{{.*}}":{168:16-168:24}:"(unsigned int)"
|
||||
}
|
||||
|
||||
void testCapitals() {
|
||||
printf("%D", 1); // no-warning
|
||||
printf("%U", 1); // no-warning
|
||||
printf("%O", 1); // no-warning
|
||||
|
||||
printf("%lD", 1); // expected-warning{{format specifies type 'long' but the argument has type 'int'}}
|
||||
|
||||
// CHECK: fix-it:"{{.*}}":{188:11-188:14}:"%D"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify -triple i386-apple-darwin9 -pedantic -DALLOWED %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -triple thumbv6-apple-ios4.0 -pedantic -DALLOWED %s
|
||||
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-mingw32 -pedantic %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -triple i686-pc-win32 -pedantic %s
|
||||
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -triple i686-linux-gnu -pedantic %s
|
||||
// RUN: %clang_cc1 -fsyntax-only -verify -triple x86_64-unknown-freebsd -pedantic %s
|
||||
|
||||
int printf(const char *restrict, ...);
|
||||
int scanf(const char * restrict, ...) ;
|
||||
|
||||
void test() {
|
||||
int justRight = 1;
|
||||
long tooLong = 2;
|
||||
|
||||
printf("%D", justRight);
|
||||
printf("%D", tooLong);
|
||||
printf("%U", justRight);
|
||||
printf("%U", tooLong);
|
||||
printf("%O", justRight);
|
||||
printf("%O", tooLong);
|
||||
|
||||
#ifdef ALLOWED
|
||||
// expected-warning@-8 {{'D' conversion specifier is not supported by ISO C}}
|
||||
// expected-warning@-8 {{'D' conversion specifier is not supported by ISO C}} expected-warning@-8 {{format specifies type 'int' but the argument has type 'long'}}
|
||||
// expected-warning@-8 {{'U' conversion specifier is not supported by ISO C}}
|
||||
// expected-warning@-8 {{'U' conversion specifier is not supported by ISO C}} expected-warning@-8 {{format specifies type 'unsigned int' but the argument has type 'long'}}
|
||||
// expected-warning@-8 {{'O' conversion specifier is not supported by ISO C}}
|
||||
// expected-warning@-8 {{'O' conversion specifier is not supported by ISO C}} expected-warning@-8 {{format specifies type 'unsigned int' but the argument has type 'long'}}
|
||||
#else
|
||||
// expected-warning@-15 {{invalid conversion specifier 'D'}}
|
||||
// expected-warning@-15 {{invalid conversion specifier 'D'}}
|
||||
// expected-warning@-15 {{invalid conversion specifier 'U'}}
|
||||
// expected-warning@-15 {{invalid conversion specifier 'U'}}
|
||||
// expected-warning@-15 {{invalid conversion specifier 'O'}}
|
||||
// expected-warning@-15 {{invalid conversion specifier 'O'}}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef ALLOWED
|
||||
void testPrintf(short x, long y) {
|
||||
printf("%hD", x); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
printf("%lD", y); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
printf("%hU", x); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
printf("%lU", y); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
printf("%hO", x); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
printf("%lO", y); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
|
||||
printf("%+'0.5lD", y); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
printf("% '0.5lD", y); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
printf("%#0.5lO", y); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
printf("%'0.5lU", y); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
}
|
||||
|
||||
void testScanf(short *x, long *y) {
|
||||
scanf("%hD", x); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
scanf("%lD", y); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
scanf("%hU", x); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
scanf("%lU", y); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
scanf("%hO", x); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
scanf("%lO", y); // expected-warning{{conversion specifier is not supported by ISO C}}
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue