Add skeleton for a more structured way to analyzing pring format
strings than what we currently have in Sema. This is both an
experiment and a WIP.
The idea is simple: parse the format string incrementally,
constructing a well-structure representation of each format specifier.
Each format specifier is then handed back one-by-one to a client via a
callback. Malformed format strings are also handled with callbacks.
The idea is to separate the parsing of the format string from the
emission of diagnostics. Currently what we have in Sema for handling
format strings is a mongrel of both that is hard to follow and
difficult to modify (I can apply this label since I'm the original
author of that code).
This is in libAnalysis as it is reasonable generic and can potentially
be used both by libSema and libChecker.
Comments welcome.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@94702 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Analysis/Analyses/PrintfFormatString.h b/include/clang/Analysis/Analyses/PrintfFormatString.h
new file mode 100644
index 0000000..978486d
--- /dev/null
+++ b/include/clang/Analysis/Analyses/PrintfFormatString.h
@@ -0,0 +1,182 @@
+//==- PrintfFormatStrings.h - Analysis of printf format strings --*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Handling of format string in printf and friends. The structure of format
+// strings for fprintf() are described in C99 7.19.6.1.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_FPRINTF_FORMAT_H
+#define LLVM_CLANG_FPRINTF_FORMAT_H
+
+#include <cassert>
+
+namespace clang {
+namespace printf {
+
+class ConversionSpecifier {
+public:
+ enum Kind {
+ InvalidSpecifier = 0,
+ dArg, // 'd'
+ iArg, // 'i',
+ oArg, // 'o',
+ uArg, // 'u',
+ xArg, // 'x',
+ XArg, // 'X',
+ fArg, // 'f',
+ FArg, // 'F',
+ eArg, // 'e',
+ EArg, // 'E',
+ gArg, // 'g',
+ GArg, // 'G',
+ aArg, // 'a',
+ AArg, // 'A',
+ IntAsCharArg, // 'c'
+ CStrArg, // 's'
+ VoidPtrArg, // 'p'
+ OutIntPtrArg, // 'n'
+ PercentArg, // '%'
+ IntArgBeg = dArg,
+ IntArgEnd = iArg,
+ UIntArgBeg = oArg,
+ UIntArgEnd = XArg,
+ DoubleArgBeg = fArg,
+ DoubleArgEnd = AArg
+ };
+
+ ConversionSpecifier(Kind k) : kind(k) {}
+
+ bool isIntArg() const { return kind >= dArg && kind <= iArg; }
+ bool isUIntArg() const { return kind >= oArg && kind <= XArg; }
+ bool isDoubleArg() const { return kind >= fArg && kind <= AArg; }
+ Kind getKind() const { return kind; }
+
+private:
+ const Kind kind;
+};
+
+enum LengthModifier {
+ None,
+ AsChar, // 'hh'
+ AsShort, // 'h'
+ AsLong, // 'l'
+ AsLongLong, // 'll'
+ AsIntMax, // 'j'
+ AsSizeT, // 'z'
+ AsPtrDiff, // 't'
+ AsLongDouble // 'L'
+};
+
+enum Flags {
+ LeftJustified = 0x1,
+ PlusPrefix = 0x2,
+ SpacePrefix = 0x4,
+ AlternativeForm = 0x8,
+ LeadingZeroes = 0x16
+};
+
+class OptionalAmount {
+public:
+ enum HowSpecified { NotSpecified, Constant, Arg };
+
+ OptionalAmount(HowSpecified h = NotSpecified) : hs(h), amt(0) {}
+ OptionalAmount(unsigned i) : hs(Constant), amt(i) {}
+
+ HowSpecified getHowSpecified() const { return hs; }
+
+ unsigned getConstantAmount() const {
+ assert(hs == Constant);
+ return amt;
+ }
+
+ unsigned getArgumentsConsumed() {
+ return hs == Arg ? 1 : 0;
+ }
+
+private:
+ HowSpecified hs;
+ unsigned amt;
+};
+
+class FormatSpecifier {
+ unsigned conversionSpecifier : 6;
+ unsigned lengthModifier : 5;
+ unsigned flags : 5;
+ OptionalAmount FieldWidth;
+ OptionalAmount Precision;
+public:
+ FormatSpecifier() : conversionSpecifier(0), lengthModifier(0), flags(0) {}
+
+ static FormatSpecifier Parse(const char *beg, const char *end);
+
+ // Methods for incrementally constructing the FormatSpecifier.
+ void setConversionSpecifier(ConversionSpecifier cs) {
+ conversionSpecifier = (unsigned) cs.getKind();
+ }
+ void setLengthModifier(LengthModifier lm) {
+ lengthModifier = (unsigned) lm;
+ }
+ void setIsLeftJustified() { flags |= LeftJustified; }
+ void setHasPlusPrefix() { flags |= PlusPrefix; }
+ void setHasSpacePrefix() { flags |= SpacePrefix; }
+ void setHasAlternativeForm() { flags |= AlternativeForm; }
+ void setHasLeadingZeros() { flags |= LeadingZeroes; }
+
+ // Methods for querying the format specifier.
+
+ ConversionSpecifier getConversionSpecifier() const {
+ return (ConversionSpecifier::Kind) conversionSpecifier;
+ }
+
+ LengthModifier getLengthModifier() const {
+ return (LengthModifier) lengthModifier;
+ }
+
+ void setFieldWidth(const OptionalAmount &Amt) {
+ FieldWidth = Amt;
+ }
+
+ void setPrecision(const OptionalAmount &Amt) {
+ Precision = Amt;
+ }
+
+ bool isLeftJustified() const { return flags & LeftJustified; }
+ bool hasPlusPrefix() const { return flags & PlusPrefix; }
+ bool hasAlternativeForm() const { return flags & AlternativeForm; }
+ bool hasLeadingZeros() const { return flags & LeadingZeroes; }
+};
+
+
+class FormatStringHandler {
+public:
+ FormatStringHandler() {}
+ virtual ~FormatStringHandler();
+
+ virtual void HandleIncompleteFormatSpecifier(const char *startSpecifier,
+ const char *endSpecifier) {}
+
+ virtual void HandleNullChar(const char *nullCharacter) {}
+
+ virtual void HandleIncompletePrecision(const char *periodChar) {}
+
+ virtual void HandleInvalidConversionSpecifier(const char *conversionChar) {}
+
+ virtual void HandleFormatSpecifier(const FormatSpecifier &FS,
+ const char *startSpecifier,
+ const char *endSpecifier) {}
+};
+
+bool ParseFormatString(FormatStringHandler &H,
+ const char *beg, const char *end);
+
+
+} // end printf namespace
+} // end clang namespace
+#endif
diff --git a/lib/Analysis/CMakeLists.txt b/lib/Analysis/CMakeLists.txt
index 0cadca5..4f8259e 100644
--- a/lib/Analysis/CMakeLists.txt
+++ b/lib/Analysis/CMakeLists.txt
@@ -4,6 +4,7 @@
AnalysisContext.cpp
CFG.cpp
LiveVariables.cpp
+ PrintfFormatString.cpp
UninitializedValues.cpp
)
diff --git a/lib/Analysis/PrintfFormatString.cpp b/lib/Analysis/PrintfFormatString.cpp
new file mode 100644
index 0000000..b2adeeb
--- /dev/null
+++ b/lib/Analysis/PrintfFormatString.cpp
@@ -0,0 +1,239 @@
+//= PrintfFormatStrings.cpp - Analysis of printf format strings --*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Handling of format string in printf and friends. The structure of format
+// strings for fprintf() are described in C99 7.19.6.1.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/Analyses/PrintfFormatString.h"
+
+using namespace clang;
+using namespace printf;
+
+namespace {
+class FormatSpecifierResult {
+ FormatSpecifier FS;
+ const char *Start;
+ bool HasError;
+public:
+ FormatSpecifierResult(bool err = false)
+ : Start(0), HasError(err) {}
+ FormatSpecifierResult(const char *start,
+ const printf::FormatSpecifier &fs)
+ : FS(fs), Start(start), HasError(false) {}
+
+
+ const char *getStart() const { return Start; }
+ bool hasError() const { return HasError; }
+ bool hasValue() const { return Start != 0; }
+ const FormatSpecifier &getValue() const {
+ assert(hasValue());
+ return FS;
+ }
+ const printf::FormatSpecifier &getValue() { return FS; }
+};
+} // end anonymous namespace
+
+template <typename T>
+class UpdateOnReturn {
+ T &ValueToUpdate;
+ const T &ValueToCopy;
+public:
+ UpdateOnReturn(T &valueToUpdate, const T &valueToCopy)
+ : ValueToUpdate(valueToUpdate), ValueToCopy(valueToCopy) {}
+
+ ~UpdateOnReturn() {
+ ValueToUpdate = ValueToCopy;
+ }
+};
+
+static OptionalAmount ParseAmount(const char *&Beg, const char *E) {
+ const char *I = Beg;
+ UpdateOnReturn <const char*> UpdateBeg(Beg, I);
+
+ bool foundDigits = false;
+ unsigned accumulator = 0;
+
+ for ( ; I != E; ++I) {
+ char c = *I;
+ if (c >= '0' && c <= '9') {
+ foundDigits = true;
+ accumulator += (accumulator * 10) + (c - '0');
+ continue;
+ }
+
+ if (foundDigits)
+ return OptionalAmount(accumulator);
+
+ if (c == '*')
+ return OptionalAmount(OptionalAmount::Arg);
+
+ break;
+ }
+
+ return OptionalAmount();
+}
+
+static FormatSpecifierResult ParseFormatSpecifier(printf::FormatStringHandler &H,
+ const char *&Beg, const char *E) {
+
+ const char *I = Beg;
+ const char *Start = NULL;
+ UpdateOnReturn <const char*> UpdateBeg(Beg, I);
+
+ // Look for a '%' character that indicates the start of a format specifier.
+ while (I != E) {
+ char c = *I;
+ ++I;
+ if (c == '\0') {
+ // Detect spurious null characters, which are likely errors.
+ H.HandleNullChar(I);
+ return true;
+ }
+ if (c == '%') {
+ Start = I; // Record the start of the format specifier.
+ break;
+ }
+ }
+
+ // No format specifier found?
+ if (!Start)
+ return false;
+
+ if (I == E) {
+ // No more characters left?
+ H.HandleIncompleteFormatSpecifier(Start, E);
+ return true;
+ }
+
+ FormatSpecifier FS;
+
+ // Look for flags (if any).
+ bool hasMore = true;
+ 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;
+ }
+ if (!hasMore)
+ break;
+ }
+
+ if (I == E) {
+ // No more characters left?
+ H.HandleIncompleteFormatSpecifier(Start, E);
+ return true;
+ }
+
+ // Look for the field width (if any).
+ FS.setFieldWidth(ParseAmount(I, E));
+
+ if (I == E) {
+ // No more characters left?
+ H.HandleIncompleteFormatSpecifier(Start, E);
+ return true;
+ }
+
+ // Look for the precision (if any).
+ if (*I == '.') {
+ const char *startPrecision = I++;
+ if (I == E) {
+ H.HandleIncompletePrecision(I - 1);
+ return true;
+ }
+
+ FS.setPrecision(ParseAmount(I, E));
+
+ if (I == E) {
+ // No more characters left?
+ H.HandleIncompletePrecision(startPrecision);
+ return true;
+ }
+ }
+
+ // Look for the length modifier.
+ LengthModifier lm = None;
+ switch (*I) {
+ default:
+ break;
+ case 'h':
+ ++I;
+ lm = (I != E && *I == 'h') ? ++I, AsChar : AsShort;
+ break;
+ case 'l':
+ ++I;
+ lm = (I != E && *I == 'l') ? ++I, AsLongLong : AsLong;
+ break;
+ case 'j': lm = AsIntMax; ++I; break;
+ case 'z': lm = AsSizeT; ++I; break;
+ case 't': lm = AsPtrDiff; ++I; break;
+ case 'L': lm = AsLongDouble; ++I; break;
+ }
+ FS.setLengthModifier(lm);
+
+ if (I == E) {
+ // No more characters left?
+ H.HandleIncompleteFormatSpecifier(Start, E);
+ return true;
+ }
+
+ // Finally, look for the conversion specifier.
+ ConversionSpecifier::Kind cs;
+ switch (*I) {
+ default:
+ H.HandleInvalidConversionSpecifier(I);
+ return true;
+ case 'd' : cs = ConversionSpecifier::dArg; break;
+ case 'i' : cs = ConversionSpecifier::iArg; break;
+ case 'o' : cs = ConversionSpecifier::oArg; break;
+ case 'u' : cs = ConversionSpecifier::uArg; break;
+ case 'x' : cs = ConversionSpecifier::xArg; break;
+ case 'X' : cs = ConversionSpecifier::XArg; break;
+ case 'f' : cs = ConversionSpecifier::fArg; break;
+ case 'F' : cs = ConversionSpecifier::FArg; break;
+ case 'e' : cs = ConversionSpecifier::eArg; break;
+ case 'E' : cs = ConversionSpecifier::EArg; break;
+ case 'g' : cs = ConversionSpecifier::gArg; break;
+ case 'G' : cs = ConversionSpecifier::GArg; break;
+ case 'a' : cs = ConversionSpecifier::aArg; break;
+ case 'A' : cs = ConversionSpecifier::AArg; break;
+ case 'c' : cs = ConversionSpecifier::IntAsCharArg; break;
+ case 's' : cs = ConversionSpecifier::CStrArg; break;
+ case 'p' : cs = ConversionSpecifier::VoidPtrArg; break;
+ case 'n' : cs = ConversionSpecifier::OutIntPtrArg; break;
+ case '%' : cs = ConversionSpecifier::PercentArg; break;
+ }
+ FS.setConversionSpecifier(cs);
+ return FormatSpecifierResult(Start, FS);
+}
+
+bool ParseFormatSring(FormatStringHandler &H, const char *I, const char *E) {
+ // Keep looking for a format specifier until we have exhausted the string.
+ while (I != E) {
+ const FormatSpecifierResult &FSR = ParseFormatSpecifier(H, I, E);
+ // Did an error of any kind occur when parsing the specifier? If so,
+ // don't do any more processing.
+ if (FSR.hasError())
+ return true;;
+ // Done processing the string?
+ if (!FSR.hasValue())
+ break;
+ // We have a format specifier. Pass it to the callback.
+ H.HandleFormatSpecifier(FSR.getValue(), FSR.getStart(), I);
+ }
+ assert(I == E && "Format string not exhausted");
+ return false;
+}
+
+FormatStringHandler::~FormatStringHandler() {}