OptionalAmount(HowSpecified howSpecified,
unsigned amount,
const char *amountStart,
+ unsigned amountLength,
bool usesPositionalArg)
- : start(amountStart), hs(howSpecified), amt(amount),
+ : start(amountStart), length(amountLength), hs(howSpecified), amt(amount),
UsesPositionalArg(usesPositionalArg) {}
OptionalAmount(bool valid = true)
- : start(0), hs(valid ? NotSpecified : Invalid), amt(0),
+ : start(0),length(0), hs(valid ? NotSpecified : Invalid), amt(0),
UsesPositionalArg(0) {}
bool isInvalid() const {
return start;
}
+ unsigned getConstantLength() const {
+ assert(hs == Constant);
+ return length;
+ }
+
ArgTypeResult getArgType(ASTContext &Ctx) const;
void toString(llvm::raw_ostream &os) const;
private:
const char *start;
+ unsigned length;
HowSpecified hs;
unsigned amt;
bool UsesPositionalArg : 1;
};
+// Class representing optional flags with location and representation
+// information.
+class OptionalFlag {
+public:
+ OptionalFlag(const char *Representation)
+ : representation(Representation), flag(false) {}
+ bool isSet() { return flag; }
+ void set() { flag = true; }
+ void clear() { flag = false; }
+ void setPosition(const char *position) {
+ assert(position);
+ this->position = position;
+ }
+ const char *getPosition() const {
+ assert(position);
+ return position;
+ }
+ const char *toString() const { return representation; }
+
+ // Overloaded operators for bool like qualities
+ operator bool() const { return flag; }
+ OptionalFlag& operator=(const bool &rhs) {
+ flag = rhs;
+ return *this; // Return a reference to myself.
+ }
+private:
+ const char *representation;
+ const char *position;
+ bool flag;
+};
+
class FormatSpecifier {
LengthModifier LM;
- unsigned IsLeftJustified : 1;
- unsigned HasPlusPrefix : 1;
- unsigned HasSpacePrefix : 1;
- unsigned HasAlternativeForm : 1;
- unsigned HasLeadingZeroes : 1;
+ OptionalFlag IsLeftJustified; // '-'
+ OptionalFlag HasPlusPrefix; // '+'
+ OptionalFlag HasSpacePrefix; // ' '
+ OptionalFlag HasAlternativeForm; // '#'
+ OptionalFlag HasLeadingZeroes; // '0'
/// Positional arguments, an IEEE extension:
/// IEEE Std 1003.1, 2004 Edition
/// http://www.opengroup.org/onlinepubs/009695399/functions/printf.html
- unsigned UsesPositionalArg : 1;
+ bool UsesPositionalArg;
unsigned argIndex;
ConversionSpecifier CS;
OptionalAmount FieldWidth;
OptionalAmount Precision;
public:
FormatSpecifier() :
- IsLeftJustified(0), HasPlusPrefix(0), HasSpacePrefix(0),
- HasAlternativeForm(0), HasLeadingZeroes(0), UsesPositionalArg(0),
+ IsLeftJustified("-"), HasPlusPrefix("+"), HasSpacePrefix(" "),
+ HasAlternativeForm("#"), HasLeadingZeroes("0"), UsesPositionalArg(false),
argIndex(0) {}
static FormatSpecifier Parse(const char *beg, const char *end);
void setLengthModifier(LengthModifier lm) {
LM = lm;
}
- void setIsLeftJustified() { IsLeftJustified = 1; }
- void setHasPlusPrefix() { HasPlusPrefix = 1; }
- void setHasSpacePrefix() { HasSpacePrefix = 1; }
- void setHasAlternativeForm() { HasAlternativeForm = 1; }
- void setHasLeadingZeros() { HasLeadingZeroes = 1; }
- void setUsesPositionalArg() { UsesPositionalArg = 1; }
+ void setIsLeftJustified(const char *position) {
+ IsLeftJustified = true;
+ IsLeftJustified.setPosition(position);
+ }
+ void setHasPlusPrefix(const char *position) {
+ HasPlusPrefix = true;
+ HasPlusPrefix.setPosition(position);
+ }
+ void setHasSpacePrefix(const char *position) {
+ HasSpacePrefix = true;
+ HasSpacePrefix.setPosition(position);
+ }
+ void setHasAlternativeForm(const char *position) {
+ HasAlternativeForm = true;
+ HasAlternativeForm.setPosition(position);
+ }
+ void setHasLeadingZeros(const char *position) {
+ HasLeadingZeroes = true;
+ HasLeadingZeroes.setPosition(position);
+ }
+ void setUsesPositionalArg() { UsesPositionalArg = true; }
void setArgIndex(unsigned i) {
assert(CS.consumesDataArgument());
return CS;
}
- LengthModifier getLengthModifier() const {
+ const LengthModifier &getLengthModifier() const {
return LM;
}
/// more than one type.
ArgTypeResult getArgType(ASTContext &Ctx) const;
- bool isLeftJustified() const { return (bool) IsLeftJustified; }
- bool hasPlusPrefix() const { return (bool) HasPlusPrefix; }
- bool hasAlternativeForm() const { return (bool) HasAlternativeForm; }
- bool hasLeadingZeros() const { return (bool) HasLeadingZeroes; }
- bool hasSpacePrefix() const { return (bool) HasSpacePrefix; }
- bool usesPositionalArg() const { return (bool) UsesPositionalArg; }
+ const OptionalFlag &isLeftJustified() const { return IsLeftJustified; }
+ const OptionalFlag &hasPlusPrefix() const { return HasPlusPrefix; }
+ const OptionalFlag &hasAlternativeForm() const { return HasAlternativeForm; }
+ const OptionalFlag &hasLeadingZeros() const { return HasLeadingZeroes; }
+ const OptionalFlag &hasSpacePrefix() const { return HasSpacePrefix; }
+ bool usesPositionalArg() const { return UsesPositionalArg; }
/// Changes the specifier and length according to a QualType, retaining any
/// flags or options. Returns true on success, or false when a conversion
bool fixType(QualType QT);
void toString(llvm::raw_ostream &os) const;
+
+ // Validation methods - to check if any element results in undefined behavior
+ bool hasValidPlusPrefix() const;
+ bool hasValidAlternativeForm() const;
+ bool hasValidLeadingZeros() const;
+ bool hasValidSpacePrefix() const;
+ bool hasValidLeftJustified() const;
+
+ bool hasValidLengthModifier() const;
+ bool hasValidPrecision() const;
+ bool hasValidFieldWidth() const;
};
enum PositionContext { FieldWidthPos = 0, PrecisionPos = 1 };
def warn_printf_asterisk_wrong_type : Warning<
"field %select{width|precision}0 should have type %1, but argument has type %2">,
InGroup<Format>;
-def warn_printf_nonsensical_precision: Warning<
- "precision used in '%0' conversion specifier (where it has no meaning)">,
+def warn_printf_nonsensical_optional_amount: Warning<
+ "%select{field width|precision}0 used with '%1' conversion specifier, resulting in undefined behavior">,
InGroup<Format>;
def warn_printf_nonsensical_flag: Warning<
- "flag '%0' results in undefined behavior in '%1' conversion specifier">,
+ "flag '%0' results in undefined behavior with '%1' conversion specifier">,
+ InGroup<Format>;
+def warn_printf_nonsensical_length: Warning<
+ "length modifier '%0' results in undefined behavior or no effect with '%1' conversion specifier">,
+ InGroup<Format>;
+def warn_printf_ignored_flag: Warning<
+ "flag '%0' is ignored when flag '%1' is present">,
InGroup<Format>;
// CHECK: returning address/reference of stack memory
}
if (hasDigits)
- return OptionalAmount(OptionalAmount::Constant, accumulator, Beg, 0);
+ return OptionalAmount(OptionalAmount::Constant, accumulator, Beg, I - Beg,
+ false);
break;
}
unsigned &argIndex) {
if (*Beg == '*') {
++Beg;
- return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0);
+ return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false);
}
return ParseAmount(Beg, E);
Beg = ++I;
return OptionalAmount(OptionalAmount::Arg, Amt.getConstantAmount() - 1,
- Tmp, 1);
+ Tmp, 0, true);
}
H.HandleInvalidPosition(Beg, I - Beg, p);
for ( ; I != E; ++I) {
switch (*I) {
default: hasMore = false; break;
- case '-': FS.setIsLeftJustified(); break;
- case '+': FS.setHasPlusPrefix(); break;
- case ' ': FS.setHasSpacePrefix(); break;
- case '#': FS.setHasAlternativeForm(); break;
- case '0': FS.setHasLeadingZeros(); break;
+ case '-': FS.setIsLeftJustified(I); break;
+ case '+': FS.setHasPlusPrefix(I); break;
+ case ' ': FS.setHasSpacePrefix(I); break;
+ case '#': FS.setHasAlternativeForm(I); break;
+ case '0': FS.setHasLeadingZeros(I); break;
}
if (!hasMore)
break;
return;
case Arg:
if (usesPositionalArg())
- os << ".*" << getPositionalArgIndex() << "$";
+ os << "*" << getPositionalArgIndex() << "$";
else
- os << ".*";
+ os << "*";
break;
case Constant:
- os << "." << amt;
+ os << amt;
break;
}
}
Precision.setHowSpecified(OptionalAmount::NotSpecified);
HasAlternativeForm = 0;
HasLeadingZeroes = 0;
+ HasPlusPrefix = 0;
}
// Test for Floating type first as LongDouble can pass isUnsignedIntegerType
else if (QT->isFloatingType()) {
Precision.setHowSpecified(OptionalAmount::NotSpecified);
HasAlternativeForm = 0;
HasLeadingZeroes = 0;
+ HasPlusPrefix = 0;
}
else if (QT->isSignedIntegerType()) {
CS.setKind(ConversionSpecifier::dArg);
else if (QT->isUnsignedIntegerType()) {
CS.setKind(ConversionSpecifier::uArg);
HasAlternativeForm = 0;
+ HasPlusPrefix = 0;
}
else {
return false;
// Conversion specifier
os << CS.toString();
}
+
+bool FormatSpecifier::hasValidPlusPrefix() const {
+ if (!HasPlusPrefix)
+ return true;
+
+ // The plus prefix only makes sense for signed conversions
+ switch (CS.getKind()) {
+ case ConversionSpecifier::dArg:
+ case ConversionSpecifier::iArg:
+ case ConversionSpecifier::fArg:
+ case ConversionSpecifier::FArg:
+ case ConversionSpecifier::eArg:
+ case ConversionSpecifier::EArg:
+ case ConversionSpecifier::gArg:
+ case ConversionSpecifier::GArg:
+ case ConversionSpecifier::aArg:
+ case ConversionSpecifier::AArg:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool FormatSpecifier::hasValidAlternativeForm() const {
+ if (!HasAlternativeForm)
+ return true;
+
+ // Alternate form flag only valid with the oxaAeEfFgG conversions
+ switch (CS.getKind()) {
+ case ConversionSpecifier::oArg:
+ case ConversionSpecifier::xArg:
+ case ConversionSpecifier::aArg:
+ case ConversionSpecifier::AArg:
+ case ConversionSpecifier::eArg:
+ case ConversionSpecifier::EArg:
+ case ConversionSpecifier::fArg:
+ case ConversionSpecifier::FArg:
+ case ConversionSpecifier::gArg:
+ case ConversionSpecifier::GArg:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool FormatSpecifier::hasValidLeadingZeros() const {
+ if (!HasLeadingZeroes)
+ return true;
+
+ // Leading zeroes flag only valid with the diouxXaAeEfFgG conversions
+ switch (CS.getKind()) {
+ case ConversionSpecifier::dArg:
+ case ConversionSpecifier::iArg:
+ case ConversionSpecifier::oArg:
+ case ConversionSpecifier::uArg:
+ case ConversionSpecifier::xArg:
+ case ConversionSpecifier::XArg:
+ case ConversionSpecifier::aArg:
+ case ConversionSpecifier::AArg:
+ case ConversionSpecifier::eArg:
+ case ConversionSpecifier::EArg:
+ case ConversionSpecifier::fArg:
+ case ConversionSpecifier::FArg:
+ case ConversionSpecifier::gArg:
+ case ConversionSpecifier::GArg:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool FormatSpecifier::hasValidSpacePrefix() const {
+ if (!HasSpacePrefix)
+ return true;
+
+ // The space prefix only makes sense for signed conversions
+ switch (CS.getKind()) {
+ case ConversionSpecifier::dArg:
+ case ConversionSpecifier::iArg:
+ case ConversionSpecifier::fArg:
+ case ConversionSpecifier::FArg:
+ case ConversionSpecifier::eArg:
+ case ConversionSpecifier::EArg:
+ case ConversionSpecifier::gArg:
+ case ConversionSpecifier::GArg:
+ case ConversionSpecifier::aArg:
+ case ConversionSpecifier::AArg:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool FormatSpecifier::hasValidLeftJustified() const {
+ if (!IsLeftJustified)
+ return true;
+
+ // The left justified flag is valid for all conversions except n
+ switch (CS.getKind()) {
+ case ConversionSpecifier::OutIntPtrArg:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+bool FormatSpecifier::hasValidLengthModifier() const {
+ switch (LM.getKind()) {
+ case LengthModifier::None:
+ return true;
+
+ // Handle most integer flags
+ case LengthModifier::AsChar:
+ case LengthModifier::AsShort:
+ case LengthModifier::AsLongLong:
+ case LengthModifier::AsIntMax:
+ case LengthModifier::AsSizeT:
+ case LengthModifier::AsPtrDiff:
+ switch (CS.getKind()) {
+ case ConversionSpecifier::dArg:
+ case ConversionSpecifier::iArg:
+ case ConversionSpecifier::oArg:
+ case ConversionSpecifier::uArg:
+ case ConversionSpecifier::xArg:
+ case ConversionSpecifier::XArg:
+ case ConversionSpecifier::OutIntPtrArg:
+ return true;
+ default:
+ return false;
+ }
+
+ // Handle 'l' flag
+ case LengthModifier::AsLong:
+ switch (CS.getKind()) {
+ case ConversionSpecifier::dArg:
+ case ConversionSpecifier::iArg:
+ case ConversionSpecifier::oArg:
+ case ConversionSpecifier::uArg:
+ case ConversionSpecifier::xArg:
+ case ConversionSpecifier::XArg:
+ case ConversionSpecifier::aArg:
+ case ConversionSpecifier::AArg:
+ case ConversionSpecifier::fArg:
+ case ConversionSpecifier::FArg:
+ case ConversionSpecifier::eArg:
+ case ConversionSpecifier::EArg:
+ case ConversionSpecifier::gArg:
+ case ConversionSpecifier::GArg:
+ case ConversionSpecifier::OutIntPtrArg:
+ case ConversionSpecifier::IntAsCharArg:
+ case ConversionSpecifier::CStrArg:
+ return true;
+ default:
+ return false;
+ }
+
+ case LengthModifier::AsLongDouble:
+ switch (CS.getKind()) {
+ case ConversionSpecifier::aArg:
+ case ConversionSpecifier::AArg:
+ case ConversionSpecifier::fArg:
+ case ConversionSpecifier::FArg:
+ case ConversionSpecifier::eArg:
+ case ConversionSpecifier::EArg:
+ case ConversionSpecifier::gArg:
+ case ConversionSpecifier::GArg:
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+bool FormatSpecifier::hasValidPrecision() const {
+ if (Precision.getHowSpecified() == OptionalAmount::NotSpecified)
+ return true;
+
+ // Precision is only valid with the diouxXaAeEfFgGs conversions
+ switch (CS.getKind()) {
+ case ConversionSpecifier::dArg:
+ case ConversionSpecifier::iArg:
+ case ConversionSpecifier::oArg:
+ case ConversionSpecifier::uArg:
+ case ConversionSpecifier::xArg:
+ case ConversionSpecifier::XArg:
+ case ConversionSpecifier::aArg:
+ case ConversionSpecifier::AArg:
+ case ConversionSpecifier::eArg:
+ case ConversionSpecifier::EArg:
+ case ConversionSpecifier::fArg:
+ case ConversionSpecifier::FArg:
+ case ConversionSpecifier::gArg:
+ case ConversionSpecifier::GArg:
+ case ConversionSpecifier::CStrArg:
+ return true;
+
+ default:
+ return false;
+ }
+}
+bool FormatSpecifier::hasValidFieldWidth() const {
+ if (FieldWidth.getHowSpecified() == OptionalAmount::NotSpecified)
+ return true;
+
+ // The field width is valid for all conversions except n
+ switch (CS.getKind()) {
+ case ConversionSpecifier::OutIntPtrArg:
+ return false;
+
+ default:
+ return true;
+ }
+}
bool HandleAmount(const analyze_printf::OptionalAmount &Amt, unsigned k,
const char *startSpecifier, unsigned specifierLen);
- void HandleFlags(const analyze_printf::FormatSpecifier &FS,
- llvm::StringRef flag, llvm::StringRef cspec,
- const char *startSpecifier, unsigned specifierLen);
+ void HandleInvalidAmount(const analyze_printf::FormatSpecifier &FS,
+ const analyze_printf::OptionalAmount &Amt,
+ unsigned type,
+ const char *startSpecifier, unsigned specifierLen);
+ void HandleFlag(const analyze_printf::FormatSpecifier &FS,
+ const analyze_printf::OptionalFlag &flag,
+ const char *startSpecifier, unsigned specifierLen);
+ void HandleIgnoredFlag(const analyze_printf::FormatSpecifier &FS,
+ const analyze_printf::OptionalFlag &ignoredFlag,
+ const analyze_printf::OptionalFlag &flag,
+ const char *startSpecifier, unsigned specifierLen);
const Expr *getDataArg(unsigned i) const;
};
return TheCall->getArg(FirstDataArg + i);
}
-void CheckPrintfHandler::HandleFlags(const analyze_printf::FormatSpecifier &FS,
- llvm::StringRef flag,
- llvm::StringRef cspec,
- const char *startSpecifier,
- unsigned specifierLen) {
- const analyze_printf::ConversionSpecifier &CS = FS.getConversionSpecifier();
- S.Diag(getLocationOfByte(CS.getStart()), diag::warn_printf_nonsensical_flag)
- << flag << cspec << getFormatSpecifierRange(startSpecifier, specifierLen);
-}
-
bool
CheckPrintfHandler::HandleAmount(const analyze_printf::OptionalAmount &Amt,
unsigned k, const char *startSpecifier,
return true;
}
+void CheckPrintfHandler::HandleInvalidAmount(
+ const analyze_printf::FormatSpecifier &FS,
+ const analyze_printf::OptionalAmount &Amt,
+ unsigned type,
+ const char *startSpecifier,
+ unsigned specifierLen) {
+ const analyze_printf::ConversionSpecifier &CS = FS.getConversionSpecifier();
+ switch (Amt.getHowSpecified()) {
+ case analyze_printf::OptionalAmount::Constant:
+ S.Diag(getLocationOfByte(Amt.getStart()),
+ diag::warn_printf_nonsensical_optional_amount)
+ << type
+ << CS.toString()
+ << getFormatSpecifierRange(startSpecifier, specifierLen)
+ << FixItHint::CreateRemoval(getFormatSpecifierRange(Amt.getStart(),
+ Amt.getConstantLength()));
+ break;
+
+ default:
+ S.Diag(getLocationOfByte(Amt.getStart()),
+ diag::warn_printf_nonsensical_optional_amount)
+ << type
+ << CS.toString()
+ << getFormatSpecifierRange(startSpecifier, specifierLen);
+ break;
+ }
+}
+
+void CheckPrintfHandler::HandleFlag(const analyze_printf::FormatSpecifier &FS,
+ const analyze_printf::OptionalFlag &flag,
+ const char *startSpecifier,
+ unsigned specifierLen) {
+ // Warn about pointless flag with a fixit removal.
+ const analyze_printf::ConversionSpecifier &CS = FS.getConversionSpecifier();
+ S.Diag(getLocationOfByte(flag.getPosition()),
+ diag::warn_printf_nonsensical_flag)
+ << flag.toString() << CS.toString()
+ << getFormatSpecifierRange(startSpecifier, specifierLen)
+ << FixItHint::CreateRemoval(getFormatSpecifierRange(flag.getPosition(), 1));
+}
+
+void CheckPrintfHandler::HandleIgnoredFlag(
+ const analyze_printf::FormatSpecifier &FS,
+ const analyze_printf::OptionalFlag &ignoredFlag,
+ const analyze_printf::OptionalFlag &flag,
+ const char *startSpecifier,
+ unsigned specifierLen) {
+ // Warn about ignored flag with a fixit removal.
+ S.Diag(getLocationOfByte(ignoredFlag.getPosition()),
+ diag::warn_printf_ignored_flag)
+ << ignoredFlag.toString() << flag.toString()
+ << getFormatSpecifierRange(startSpecifier, specifierLen)
+ << FixItHint::CreateRemoval(getFormatSpecifierRange(
+ ignoredFlag.getPosition(), 1));
+}
+
bool
CheckPrintfHandler::HandleFormatSpecifier(const analyze_printf::FormatSpecifier
&FS,
return HandleInvalidConversionSpecifier(FS, startSpecifier, specifierLen);
}
- // Are we using '%n'? Issue a warning about this being
- // a possible security issue.
+ // Check for invalid use of field width
+ if (!FS.hasValidFieldWidth()) {
+ HandleInvalidAmount(FS, FS.getFieldWidth(), /* field width */ 1,
+ startSpecifier, specifierLen);
+ }
+
+ // Check for invalid use of precision
+ if (!FS.hasValidPrecision()) {
+ HandleInvalidAmount(FS, FS.getPrecision(), /* precision */ 1,
+ startSpecifier, specifierLen);
+ }
+
+ // Check each flag does not conflict with any other component.
+ if (!FS.hasValidLeadingZeros())
+ HandleFlag(FS, FS.hasLeadingZeros(), startSpecifier, specifierLen);
+ if (!FS.hasValidPlusPrefix())
+ HandleFlag(FS, FS.hasPlusPrefix(), startSpecifier, specifierLen);
+ // FIXME: the following lines are disabled due to clang assertions on
+ // highlights containing spaces.
+ // if (!FS.hasValidSpacePrefix())
+ // HandleFlag(FS, FS.hasSpacePrefix(), startSpecifier, specifierLen);
+ if (!FS.hasValidAlternativeForm())
+ HandleFlag(FS, FS.hasAlternativeForm(), startSpecifier, specifierLen);
+ if (!FS.hasValidLeftJustified())
+ HandleFlag(FS, FS.isLeftJustified(), startSpecifier, specifierLen);
+
+ // Check that flags are not ignored by another flag
+ // FIXME: the following lines are disabled due to clang assertions on
+ // highlights containing spaces.
+ //if (FS.hasSpacePrefix() && FS.hasPlusPrefix()) // ' ' ignored by '+'
+ // HandleIgnoredFlag(FS, FS.hasSpacePrefix(), FS.hasPlusPrefix(),
+ // startSpecifier, specifierLen);
+ if (FS.hasLeadingZeros() && FS.isLeftJustified()) // '0' ignored by '-'
+ HandleIgnoredFlag(FS, FS.hasLeadingZeros(), FS.isLeftJustified(),
+ startSpecifier, specifierLen);
+
+ // Check the length modifier is valid with the given conversion specifier.
+ const LengthModifier &LM = FS.getLengthModifier();
+ if (!FS.hasValidLengthModifier())
+ S.Diag(getLocationOfByte(LM.getStart()),
+ diag::warn_printf_nonsensical_length)
+ << LM.toString() << CS.toString()
+ << getFormatSpecifierRange(startSpecifier, specifierLen)
+ << FixItHint::CreateRemoval(getFormatSpecifierRange(LM.getStart(),
+ LM.getLength()));
+
+ // Are we using '%n'?
if (CS.getKind() == ConversionSpecifier::OutIntPtrArg) {
+ // Issue a warning about this being a possible security issue.
S.Diag(getLocationOfByte(CS.getStart()), diag::warn_printf_write_back)
<< getFormatSpecifierRange(startSpecifier, specifierLen);
// Continue checking the other format specifiers.
return true;
}
- if (CS.getKind() == ConversionSpecifier::VoidPtrArg) {
- if (FS.getPrecision().getHowSpecified() != OptionalAmount::NotSpecified)
- S.Diag(getLocationOfByte(CS.getStart()),
- diag::warn_printf_nonsensical_precision)
- << CS.getCharacters()
- << getFormatSpecifierRange(startSpecifier, specifierLen);
- }
- if (CS.getKind() == ConversionSpecifier::VoidPtrArg ||
- CS.getKind() == ConversionSpecifier::CStrArg) {
- // FIXME: Instead of using "0", "+", etc., eventually get them from
- // the FormatSpecifier.
- if (FS.hasLeadingZeros())
- HandleFlags(FS, "0", CS.getCharacters(), startSpecifier, specifierLen);
- if (FS.hasPlusPrefix())
- HandleFlags(FS, "+", CS.getCharacters(), startSpecifier, specifierLen);
- if (FS.hasSpacePrefix())
- HandleFlags(FS, " ", CS.getCharacters(), startSpecifier, specifierLen);
- }
-
// The remaining checks depend on the data arguments.
if (HasVAListArg)
return true;
// RUN: %clang_cc1 -fsyntax-only -verify -Wformat-nonliteral %s
+// XFAIL: *
+// FIXME: temporarily marking as expected fail until whitespace highlighting
+// is fixed.
#include <stdarg.h>
typedef __typeof(sizeof(int)) size_t;
void test11(void *p, char *s) {
printf("%p", p); // no-warning
printf("%p", 123); // expected-warning{{conversion specifies type 'void *' but the argument has type 'int'}}
- printf("%.4p", p); // expected-warning{{precision used in 'p' conversion specifier (where it has no meaning)}}
- printf("%+p", p); // expected-warning{{flag '+' results in undefined behavior in 'p' conversion specifier}}
- printf("% p", p); // expected-warning{{flag ' ' results in undefined behavior in 'p' conversion specifier}}
- printf("%0p", p); // expected-warning{{flag '0' results in undefined behavior in 'p' conversion specifier}}
+ printf("%.4p", p); // expected-warning{{precision used with 'p' conversion specifier, resulting in undefined behavior}}
+ printf("%+p", p); // expected-warning{{flag '+' results in undefined behavior with 'p' conversion specifier}}
+ printf("% p", p); // expected-warning{{flag ' ' results in undefined behavior with 'p' conversion specifier}}
+ printf("%0p", p); // expected-warning{{flag '0' results in undefined behavior with 'p' conversion specifier}}
printf("%s", s); // no-warning
- printf("%+s", p); // expected-warning{{flag '+' results in undefined behavior in 's' conversion specifier}}
- printf("% s", p); // expected-warning{{flag ' ' results in undefined behavior in 's' conversion specifier}}
- printf("%0s", p); // expected-warning{{flag '0' results in undefined behavior in 's' conversion specifier}}
+ printf("%+s", p); // expected-warning{{flag '+' results in undefined behavior with 's' conversion specifier}}
+ printf("% s", p); // expected-warning{{flag ' ' results in undefined behavior with 's' conversion specifier}}
+ printf("%0s", p); // expected-warning{{flag '0' results in undefined behavior with 's' conversion specifier}}
}
void test12(char *b) {
void rdar8026030(FILE *fp) {
fprintf(fp, "\%"); // expected-warning{{incomplete format specifier}}
}
+
+void bug7377_bad_length_mod_usage() {
+ // Bad length modifiers
+ printf("%hhs", "foo"); // expected-warning{{length modifier 'hh' results in undefined behavior or no effect with 's' conversion specifier}}
+ printf("%1$zp", (void *)0); // expected-warning{{length modifier 'z' results in undefined behavior or no effect with 'p' conversion specifier}}
+ printf("%ls", L"foo"); // no-warning
+ printf("%#.2Lf", (long double)1.234); // no-warning
+
+ // Bad flag usage
+ printf("%#p", (void *) 0); // expected-warning{{flag '#' results in undefined behavior with 'p' conversion specifier}}
+ printf("%0d", -1); // no-warning
+ printf("%#n", (void *) 0); // expected-warning{{flag '#' results in undefined behavior with 'n' conversion specifier}} expected-warning{{use of '%n' in format string discouraged (potentially insecure)}}
+ printf("%-n", (void *) 0); // expected-warning{{flag '-' results in undefined behavior with 'n' conversion specifier}} expected-warning{{use of '%n' in format string discouraged (potentially insecure)}}
+ printf("%-p", (void *) 0); // no-warning
+
+ // Bad optional amount use
+ printf("%.2c", 'a'); // expected-warning{{precision used with 'c' conversion specifier, resulting in undefined behavior}}
+ printf("%1n", (void *) 0); // expected-warning{{precision used with 'n' conversion specifier, resulting in undefined behavior}} expected-warning{{use of '%n' in format string discouraged (potentially insecure)}}
+
+ // Ignored flags
+ printf("% +f", 1.23); // expected-warning{{flag ' ' is ignored when flag '+' is present}}
+ printf("%+ f", 1.23); // expected-warning{{flag ' ' is ignored when flag '+' is present}}
+ printf("%0-f", 1.23); // expected-warning{{flag '0' is ignored when flag '-' is present}}
+ printf("%-0f", 1.23); // expected-warning{{flag '0' is ignored when flag '-' is present}}
+ printf("%-+f", 1.23); // no-warning
+}