PrintfFormatString.cpp revision d2dcece5882215e92a80bff8a7b7fd2fd81d7086
18f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek//= PrintfFormatStrings.cpp - Analysis of printf format strings --*- C++ -*-==//
28f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek//
38f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek//                     The LLVM Compiler Infrastructure
48f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek//
58f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek// This file is distributed under the University of Illinois Open Source
68f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek// License. See LICENSE.TXT for details.
78f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek//
88f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek//===----------------------------------------------------------------------===//
98f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek//
108f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek// Handling of format string in printf and friends.  The structure of format
118f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek// strings for fprintf() are described in C99 7.19.6.1.
128f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek//
138f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek//===----------------------------------------------------------------------===//
148f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
158f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek#include "clang/Analysis/Analyses/PrintfFormatString.h"
168f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
178f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenekusing namespace clang;
18d2dcece5882215e92a80bff8a7b7fd2fd81d7086Ted Kremenekusing namespace analyze_printf;
198f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
208f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremeneknamespace {
218f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenekclass FormatSpecifierResult {
228f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  FormatSpecifier FS;
238f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  const char *Start;
248f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  bool HasError;
258f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenekpublic:
268f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  FormatSpecifierResult(bool err = false)
278f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    : Start(0), HasError(err) {}
288f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  FormatSpecifierResult(const char *start,
29d2dcece5882215e92a80bff8a7b7fd2fd81d7086Ted Kremenek                        const FormatSpecifier &fs)
308f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    : FS(fs), Start(start), HasError(false) {}
318f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
328f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
338f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  const char *getStart() const { return Start; }
348f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  bool hasError() const { return HasError; }
358f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  bool hasValue() const { return Start != 0; }
368f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  const FormatSpecifier &getValue() const {
378f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    assert(hasValue());
388f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    return FS;
398f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
40d2dcece5882215e92a80bff8a7b7fd2fd81d7086Ted Kremenek  const FormatSpecifier &getValue() { return FS; }
418f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek};
428f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek} // end anonymous namespace
438f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
448f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenektemplate <typename T>
458f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenekclass UpdateOnReturn {
468f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  T &ValueToUpdate;
478f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  const T &ValueToCopy;
488f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenekpublic:
498f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  UpdateOnReturn(T &valueToUpdate, const T &valueToCopy)
508f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    : ValueToUpdate(valueToUpdate), ValueToCopy(valueToCopy) {}
518f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
528f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  ~UpdateOnReturn() {
538f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    ValueToUpdate = ValueToCopy;
548f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
558f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek};
568f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
578f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenekstatic OptionalAmount ParseAmount(const char *&Beg, const char *E) {
588f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  const char *I = Beg;
598f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  UpdateOnReturn <const char*> UpdateBeg(Beg, I);
608f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
618f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  bool foundDigits = false;
628f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  unsigned accumulator = 0;
638f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
648f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  for ( ; I != E; ++I) {
658f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    char c = *I;
668f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    if (c >= '0' && c <= '9') {
678f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      foundDigits = true;
688f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      accumulator += (accumulator * 10) + (c - '0');
698f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      continue;
708f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    }
718f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
728f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    if (foundDigits)
738f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      return OptionalAmount(accumulator);
748f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
758f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    if (c == '*')
768f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      return OptionalAmount(OptionalAmount::Arg);
778f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
788f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    break;
798f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
808f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
818f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  return OptionalAmount();
828f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek}
838f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
84d2dcece5882215e92a80bff8a7b7fd2fd81d7086Ted Kremenekstatic FormatSpecifierResult ParseFormatSpecifier(FormatStringHandler &H,
858f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek                                                  const char *&Beg, const char *E) {
868f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
878f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  const char *I = Beg;
88c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek  const char *Start = 0;
898f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  UpdateOnReturn <const char*> UpdateBeg(Beg, I);
908f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
918f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  // Look for a '%' character that indicates the start of a format specifier.
928f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  while (I != E) {
938f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    char c = *I;
948f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    ++I;
958f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    if (c == '\0') {
968f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      // Detect spurious null characters, which are likely errors.
978f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      H.HandleNullChar(I);
988f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      return true;
998f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    }
1008f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    if (c == '%') {
1018f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      Start = I;  // Record the start of the format specifier.
1028f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      break;
1038f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    }
1048f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
1058f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1068f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  // No format specifier found?
1078f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  if (!Start)
1088f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    return false;
1098f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1108f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  if (I == E) {
1118f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    // No more characters left?
1128f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    H.HandleIncompleteFormatSpecifier(Start, E);
1138f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    return true;
1148f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
1158f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1168f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  FormatSpecifier FS;
1178f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1188f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  // Look for flags (if any).
1198f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  bool hasMore = true;
1208f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  for ( ; I != E; ++I) {
1218f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    switch (*I) {
1228f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      default: hasMore = false; break;
1238f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      case '-': FS.setIsLeftJustified(); break;
1248f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      case '+': FS.setHasPlusPrefix(); break;
1258f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      case ' ': FS.setHasSpacePrefix(); break;
1268f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      case '#': FS.setHasAlternativeForm(); break;
1278f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      case '0': FS.setHasLeadingZeros(); break;
1288f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    }
1298f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    if (!hasMore)
1308f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      break;
1318f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
1328f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1338f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  if (I == E) {
1348f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    // No more characters left?
1358f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    H.HandleIncompleteFormatSpecifier(Start, E);
1368f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    return true;
1378f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
1388f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1398f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  // Look for the field width (if any).
1408f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  FS.setFieldWidth(ParseAmount(I, E));
1418f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1428f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  if (I == E) {
1438f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    // No more characters left?
1448f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    H.HandleIncompleteFormatSpecifier(Start, E);
1458f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    return true;
1468f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
1478f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1488f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  // Look for the precision (if any).
1498f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  if (*I == '.') {
1508f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    const char *startPrecision = I++;
1518f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    if (I == E) {
1528f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      H.HandleIncompletePrecision(I - 1);
1538f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      return true;
1548f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    }
1558f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1568f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    FS.setPrecision(ParseAmount(I, E));
1578f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1588f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    if (I == E) {
1598f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      // No more characters left?
1608f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      H.HandleIncompletePrecision(startPrecision);
1618f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      return true;
1628f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    }
1638f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
1648f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1658f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  // Look for the length modifier.
1668f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  LengthModifier lm = None;
1678f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  switch (*I) {
1688f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    default:
1698f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      break;
1708f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    case 'h':
1718f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      ++I;
1728f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      lm = (I != E && *I == 'h') ? ++I, AsChar : AsShort;
1738f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      break;
1748f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    case 'l':
1758f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      ++I;
1768f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      lm = (I != E && *I == 'l') ? ++I, AsLongLong : AsLong;
1778f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      break;
1788f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    case 'j': lm = AsIntMax;     ++I; break;
1798f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    case 'z': lm = AsSizeT;      ++I; break;
1808f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    case 't': lm = AsPtrDiff;    ++I; break;
1818f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    case 'L': lm = AsLongDouble; ++I; break;
1828f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
1838f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  FS.setLengthModifier(lm);
1848f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1858f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  if (I == E) {
1868f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    // No more characters left?
1878f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    H.HandleIncompleteFormatSpecifier(Start, E);
1888f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    return true;
1898f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
1908f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
1918f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  // Finally, look for the conversion specifier.
1928f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  ConversionSpecifier::Kind cs;
1938f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  switch (*I) {
1948f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    default:
1958f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      H.HandleInvalidConversionSpecifier(I);
196c7cbb9bf8e0bf8c3191ef0b782ec198c433d2a4eTed Kremenek      return true;
197c7cbb9bf8e0bf8c3191ef0b782ec198c433d2a4eTed Kremenek    // C99: 7.19.6.1 (section 8).
198c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'd': cs = ConversionSpecifier::dArg; break;
199c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'i': cs = ConversionSpecifier::iArg; break;
200c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'o': cs = ConversionSpecifier::oArg; break;
201c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'u': cs = ConversionSpecifier::uArg; break;
202c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'x': cs = ConversionSpecifier::xArg; break;
203c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'X': cs = ConversionSpecifier::XArg; break;
204c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'f': cs = ConversionSpecifier::fArg; break;
205c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'F': cs = ConversionSpecifier::FArg; break;
206c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'e': cs = ConversionSpecifier::eArg; break;
207c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'E': cs = ConversionSpecifier::EArg; break;
208c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'g': cs = ConversionSpecifier::gArg; break;
209c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'G': cs = ConversionSpecifier::GArg; break;
210c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'a': cs = ConversionSpecifier::aArg; break;
211c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'A': cs = ConversionSpecifier::AArg; break;
212c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'c': cs = ConversionSpecifier::IntAsCharArg; break;
213c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 's': cs = ConversionSpecifier::CStrArg;      break;
214c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'p': cs = ConversionSpecifier::VoidPtrArg;   break;
215c7ae51a3cd9460db8280e8623c90614f246e46d2Ted Kremenek    case 'n': cs = ConversionSpecifier::OutIntPtrArg; break;
216c7cbb9bf8e0bf8c3191ef0b782ec198c433d2a4eTed Kremenek    case '%': cs = ConversionSpecifier::PercentArg;   break;
217c7cbb9bf8e0bf8c3191ef0b782ec198c433d2a4eTed Kremenek    // Objective-C.
218c7cbb9bf8e0bf8c3191ef0b782ec198c433d2a4eTed Kremenek    case '@': cs = ConversionSpecifier::ObjCObjArg; break;
2198f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
2208f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  FS.setConversionSpecifier(cs);
2218f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  return FormatSpecifierResult(Start, FS);
2228f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek}
2238f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
2248f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenekbool ParseFormatSring(FormatStringHandler &H, const char *I, const char *E) {
2258f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  // Keep looking for a format specifier until we have exhausted the string.
2268f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  while (I != E) {
2278f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    const FormatSpecifierResult &FSR = ParseFormatSpecifier(H, I, E);
2288f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    // Did an error of any kind occur when parsing the specifier?  If so,
2298f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    // don't do any more processing.
2308f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    if (FSR.hasError())
2318f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      return true;;
2328f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    // Done processing the string?
2338f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    if (!FSR.hasValue())
2348f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek      break;
2358f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek    // We have a format specifier.  Pass it to the callback.
236d5f2096e266b7905df660b7839e69e9ed6ba9018Ted Kremenek    if (!H.HandleFormatSpecifier(FSR.getValue(), FSR.getStart(), I))
237d5f2096e266b7905df660b7839e69e9ed6ba9018Ted Kremenek      return false;
2388f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  }
2398f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  assert(I == E && "Format string not exhausted");
2408f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek  return false;
2418f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek}
2428f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted Kremenek
2438f0a1c73fd2b83afd4b1fce6f964535b51d13659Ted KremenekFormatStringHandler::~FormatStringHandler() {}
244