1212904Sdim//== PrintfFormatString.cpp - Analysis of printf format strings --*- C++ -*-==//
2203955Srdivacky//
3203955Srdivacky//                     The LLVM Compiler Infrastructure
4203955Srdivacky//
5203955Srdivacky// This file is distributed under the University of Illinois Open Source
6203955Srdivacky// License. See LICENSE.TXT for details.
7203955Srdivacky//
8203955Srdivacky//===----------------------------------------------------------------------===//
9203955Srdivacky//
10203955Srdivacky// Handling of format string in printf and friends.  The structure of format
11203955Srdivacky// strings for fprintf() are described in C99 7.19.6.1.
12203955Srdivacky//
13203955Srdivacky//===----------------------------------------------------------------------===//
14203955Srdivacky
15212904Sdim#include "clang/Analysis/Analyses/FormatString.h"
16249423Sdim#include "FormatStringParsing.h"
17243830Sdim#include "clang/Basic/TargetInfo.h"
18203955Srdivacky
19239462Sdimusing clang::analyze_format_string::ArgType;
20212904Sdimusing clang::analyze_format_string::FormatStringHandler;
21212904Sdimusing clang::analyze_format_string::LengthModifier;
22212904Sdimusing clang::analyze_format_string::OptionalAmount;
23212904Sdimusing clang::analyze_format_string::ConversionSpecifier;
24212904Sdimusing clang::analyze_printf::PrintfSpecifier;
25204643Srdivacky
26203955Srdivackyusing namespace clang;
27203955Srdivacky
28212904Sdimtypedef clang::analyze_format_string::SpecifierResult<PrintfSpecifier>
29212904Sdim        PrintfSpecifierResult;
30203955Srdivacky
31203955Srdivacky//===----------------------------------------------------------------------===//
32203955Srdivacky// Methods for parsing format strings.
33203955Srdivacky//===----------------------------------------------------------------------===//
34203955Srdivacky
35212904Sdimusing analyze_format_string::ParseNonPositionAmount;
36203955Srdivacky
37212904Sdimstatic bool ParsePrecision(FormatStringHandler &H, PrintfSpecifier &FS,
38204643Srdivacky                           const char *Start, const char *&Beg, const char *E,
39204643Srdivacky                           unsigned *argIndex) {
40204643Srdivacky  if (argIndex) {
41204643Srdivacky    FS.setPrecision(ParseNonPositionAmount(Beg, E, *argIndex));
42226633Sdim  } else {
43204643Srdivacky    const OptionalAmount Amt = ParsePositionAmount(H, Start, Beg, E,
44212904Sdim                                           analyze_format_string::PrecisionPos);
45204643Srdivacky    if (Amt.isInvalid())
46204643Srdivacky      return true;
47204643Srdivacky    FS.setPrecision(Amt);
48204643Srdivacky  }
49204643Srdivacky  return false;
50204643Srdivacky}
51204643Srdivacky
52212904Sdimstatic PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H,
53203955Srdivacky                                                  const char *&Beg,
54204643Srdivacky                                                  const char *E,
55208987Srdivacky                                                  unsigned &argIndex,
56243830Sdim                                                  const LangOptions &LO,
57243830Sdim                                                  const TargetInfo &Target) {
58203955Srdivacky
59212904Sdim  using namespace clang::analyze_format_string;
60203955Srdivacky  using namespace clang::analyze_printf;
61203955Srdivacky
62203955Srdivacky  const char *I = Beg;
63203955Srdivacky  const char *Start = 0;
64203955Srdivacky  UpdateOnReturn <const char*> UpdateBeg(Beg, I);
65203955Srdivacky
66203955Srdivacky  // Look for a '%' character that indicates the start of a format specifier.
67203955Srdivacky  for ( ; I != E ; ++I) {
68203955Srdivacky    char c = *I;
69203955Srdivacky    if (c == '\0') {
70203955Srdivacky      // Detect spurious null characters, which are likely errors.
71203955Srdivacky      H.HandleNullChar(I);
72203955Srdivacky      return true;
73203955Srdivacky    }
74203955Srdivacky    if (c == '%') {
75203955Srdivacky      Start = I++;  // Record the start of the format specifier.
76203955Srdivacky      break;
77203955Srdivacky    }
78203955Srdivacky  }
79203955Srdivacky
80203955Srdivacky  // No format specifier found?
81203955Srdivacky  if (!Start)
82203955Srdivacky    return false;
83203955Srdivacky
84203955Srdivacky  if (I == E) {
85203955Srdivacky    // No more characters left?
86212904Sdim    H.HandleIncompleteSpecifier(Start, E - Start);
87203955Srdivacky    return true;
88203955Srdivacky  }
89203955Srdivacky
90212904Sdim  PrintfSpecifier FS;
91204643Srdivacky  if (ParseArgPosition(H, FS, Start, I, E))
92204643Srdivacky    return true;
93203955Srdivacky
94204643Srdivacky  if (I == E) {
95204643Srdivacky    // No more characters left?
96212904Sdim    H.HandleIncompleteSpecifier(Start, E - Start);
97204643Srdivacky    return true;
98204643Srdivacky  }
99204643Srdivacky
100203955Srdivacky  // Look for flags (if any).
101203955Srdivacky  bool hasMore = true;
102203955Srdivacky  for ( ; I != E; ++I) {
103203955Srdivacky    switch (*I) {
104203955Srdivacky      default: hasMore = false; break;
105218893Sdim      case '\'':
106218893Sdim        // FIXME: POSIX specific.  Always accept?
107218893Sdim        FS.setHasThousandsGrouping(I);
108218893Sdim        break;
109210299Sed      case '-': FS.setIsLeftJustified(I); break;
110210299Sed      case '+': FS.setHasPlusPrefix(I); break;
111210299Sed      case ' ': FS.setHasSpacePrefix(I); break;
112210299Sed      case '#': FS.setHasAlternativeForm(I); break;
113210299Sed      case '0': FS.setHasLeadingZeros(I); break;
114203955Srdivacky    }
115203955Srdivacky    if (!hasMore)
116203955Srdivacky      break;
117203955Srdivacky  }
118203955Srdivacky
119203955Srdivacky  if (I == E) {
120203955Srdivacky    // No more characters left?
121212904Sdim    H.HandleIncompleteSpecifier(Start, E - Start);
122203955Srdivacky    return true;
123203955Srdivacky  }
124203955Srdivacky
125203955Srdivacky  // Look for the field width (if any).
126204643Srdivacky  if (ParseFieldWidth(H, FS, Start, I, E,
127204643Srdivacky                      FS.usesPositionalArg() ? 0 : &argIndex))
128204643Srdivacky    return true;
129203955Srdivacky
130203955Srdivacky  if (I == E) {
131203955Srdivacky    // No more characters left?
132212904Sdim    H.HandleIncompleteSpecifier(Start, E - Start);
133203955Srdivacky    return true;
134203955Srdivacky  }
135203955Srdivacky
136203955Srdivacky  // Look for the precision (if any).
137203955Srdivacky  if (*I == '.') {
138203955Srdivacky    ++I;
139203955Srdivacky    if (I == E) {
140212904Sdim      H.HandleIncompleteSpecifier(Start, E - Start);
141203955Srdivacky      return true;
142203955Srdivacky    }
143203955Srdivacky
144204643Srdivacky    if (ParsePrecision(H, FS, Start, I, E,
145204643Srdivacky                       FS.usesPositionalArg() ? 0 : &argIndex))
146204643Srdivacky      return true;
147203955Srdivacky
148203955Srdivacky    if (I == E) {
149203955Srdivacky      // No more characters left?
150212904Sdim      H.HandleIncompleteSpecifier(Start, E - Start);
151203955Srdivacky      return true;
152203955Srdivacky    }
153203955Srdivacky  }
154203955Srdivacky
155203955Srdivacky  // Look for the length modifier.
156234353Sdim  if (ParseLengthModifier(FS, I, E, LO) && I == E) {
157203955Srdivacky    // No more characters left?
158212904Sdim    H.HandleIncompleteSpecifier(Start, E - Start);
159203955Srdivacky    return true;
160203955Srdivacky  }
161203955Srdivacky
162203955Srdivacky  if (*I == '\0') {
163203955Srdivacky    // Detect spurious null characters, which are likely errors.
164203955Srdivacky    H.HandleNullChar(I);
165203955Srdivacky    return true;
166203955Srdivacky  }
167203955Srdivacky
168203955Srdivacky  // Finally, look for the conversion specifier.
169203955Srdivacky  const char *conversionPosition = I++;
170203955Srdivacky  ConversionSpecifier::Kind k = ConversionSpecifier::InvalidSpecifier;
171203955Srdivacky  switch (*conversionPosition) {
172203955Srdivacky    default:
173203955Srdivacky      break;
174203955Srdivacky    // C99: 7.19.6.1 (section 8).
175204643Srdivacky    case '%': k = ConversionSpecifier::PercentArg;   break;
176204643Srdivacky    case 'A': k = ConversionSpecifier::AArg; break;
177204643Srdivacky    case 'E': k = ConversionSpecifier::EArg; break;
178204643Srdivacky    case 'F': k = ConversionSpecifier::FArg; break;
179204643Srdivacky    case 'G': k = ConversionSpecifier::GArg; break;
180204643Srdivacky    case 'X': k = ConversionSpecifier::XArg; break;
181204643Srdivacky    case 'a': k = ConversionSpecifier::aArg; break;
182212904Sdim    case 'c': k = ConversionSpecifier::cArg; break;
183203955Srdivacky    case 'd': k = ConversionSpecifier::dArg; break;
184204643Srdivacky    case 'e': k = ConversionSpecifier::eArg; break;
185204643Srdivacky    case 'f': k = ConversionSpecifier::fArg; break;
186204643Srdivacky    case 'g': k = ConversionSpecifier::gArg; break;
187203955Srdivacky    case 'i': k = ConversionSpecifier::iArg; break;
188212904Sdim    case 'n': k = ConversionSpecifier::nArg; break;
189203955Srdivacky    case 'o': k = ConversionSpecifier::oArg; break;
190263508Sdim    case 'p': k = ConversionSpecifier::pArg; break;
191263508Sdim    case 's': k = ConversionSpecifier::sArg; break;
192203955Srdivacky    case 'u': k = ConversionSpecifier::uArg; break;
193203955Srdivacky    case 'x': k = ConversionSpecifier::xArg; break;
194218893Sdim    // POSIX specific.
195204643Srdivacky    case 'C': k = ConversionSpecifier::CArg; break;
196212904Sdim    case 'S': k = ConversionSpecifier::SArg; break;
197203955Srdivacky    // Objective-C.
198203955Srdivacky    case '@': k = ConversionSpecifier::ObjCObjArg; break;
199203955Srdivacky    // Glibc specific.
200203955Srdivacky    case 'm': k = ConversionSpecifier::PrintErrno; break;
201208987Srdivacky    // FreeBSD format extensions
202243830Sdim    case 'b':
203243830Sdim      if (LO.FormatExtensions)
204243830Sdim        k = ConversionSpecifier::FreeBSDbArg; // int followed by char *
205243830Sdim      break;
206243830Sdim    case 'r':
207243830Sdim      if (LO.FormatExtensions)
208243830Sdim        k = ConversionSpecifier::FreeBSDrArg;
209243830Sdim      break;
210243830Sdim    case 'y':
211243830Sdim      if (LO.FormatExtensions)
212243830Sdim        k = ConversionSpecifier::iArg;
213243830Sdim      break;
214243830Sdim    // Apple-specific
215243830Sdim    case 'D':
216243830Sdim      if (Target.getTriple().isOSDarwin())
217243830Sdim        k = ConversionSpecifier::DArg;
218243830Sdim      else if (LO.FormatExtensions)
219243830Sdim        k = ConversionSpecifier::FreeBSDDArg; // u_char * followed by char *
220243830Sdim      break;
221243830Sdim    case 'O':
222243830Sdim      if (Target.getTriple().isOSDarwin())
223243830Sdim        k = ConversionSpecifier::OArg;
224243830Sdim      break;
225243830Sdim    case 'U':
226243830Sdim      if (Target.getTriple().isOSDarwin())
227243830Sdim        k = ConversionSpecifier::UArg;
228243830Sdim      break;
229203955Srdivacky  }
230212904Sdim  PrintfConversionSpecifier CS(conversionPosition, k);
231204643Srdivacky  FS.setConversionSpecifier(CS);
232204643Srdivacky  if (CS.consumesDataArgument() && !FS.usesPositionalArg())
233204643Srdivacky    FS.setArgIndex(argIndex++);
234208987Srdivacky  // FreeBSD extension
235243830Sdim  if (k == ConversionSpecifier::FreeBSDbArg ||
236243830Sdim      k == ConversionSpecifier::FreeBSDDArg)
237208987Srdivacky    argIndex++;
238203955Srdivacky
239203955Srdivacky  if (k == ConversionSpecifier::InvalidSpecifier) {
240204643Srdivacky    // Assume the conversion takes one argument.
241218893Sdim    return !H.HandleInvalidPrintfConversionSpecifier(FS, Start, I - Start);
242203955Srdivacky  }
243212904Sdim  return PrintfSpecifierResult(Start, FS);
244203955Srdivacky}
245203955Srdivacky
246212904Sdimbool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H,
247212904Sdim                                                     const char *I,
248212904Sdim                                                     const char *E,
249243830Sdim                                                     const LangOptions &LO,
250243830Sdim                                                     const TargetInfo &Target) {
251204643Srdivacky
252204643Srdivacky  unsigned argIndex = 0;
253204643Srdivacky
254203955Srdivacky  // Keep looking for a format specifier until we have exhausted the string.
255203955Srdivacky  while (I != E) {
256212904Sdim    const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex,
257243830Sdim                                                            LO, Target);
258203955Srdivacky    // Did a fail-stop error of any kind occur when parsing the specifier?
259203955Srdivacky    // If so, don't do any more processing.
260203955Srdivacky    if (FSR.shouldStop())
261243830Sdim      return true;
262203955Srdivacky    // Did we exhaust the string or encounter an error that
263203955Srdivacky    // we can recover from?
264203955Srdivacky    if (!FSR.hasValue())
265203955Srdivacky      continue;
266203955Srdivacky    // We have a format specifier.  Pass it to the callback.
267212904Sdim    if (!H.HandlePrintfSpecifier(FSR.getValue(), FSR.getStart(),
268203955Srdivacky                                 I - FSR.getStart()))
269203955Srdivacky      return true;
270203955Srdivacky  }
271203955Srdivacky  assert(I == E && "Format string not exhausted");
272203955Srdivacky  return false;
273203955Srdivacky}
274203955Srdivacky
275203955Srdivacky//===----------------------------------------------------------------------===//
276212904Sdim// Methods on PrintfSpecifier.
277210299Sed//===----------------------------------------------------------------------===//
278210299Sed
279239462SdimArgType PrintfSpecifier::getArgType(ASTContext &Ctx,
280239462Sdim                                    bool IsObjCLiteral) const {
281212904Sdim  const PrintfConversionSpecifier &CS = getConversionSpecifier();
282218893Sdim
283203955Srdivacky  if (!CS.consumesDataArgument())
284239462Sdim    return ArgType::Invalid();
285203955Srdivacky
286212904Sdim  if (CS.getKind() == ConversionSpecifier::cArg)
287212904Sdim    switch (LM.getKind()) {
288212904Sdim      case LengthModifier::None: return Ctx.IntTy;
289234353Sdim      case LengthModifier::AsLong:
290239462Sdim        return ArgType(ArgType::WIntTy, "wint_t");
291212904Sdim      default:
292239462Sdim        return ArgType::Invalid();
293212904Sdim    }
294218893Sdim
295203955Srdivacky  if (CS.isIntArg())
296210299Sed    switch (LM.getKind()) {
297210299Sed      case LengthModifier::AsLongDouble:
298234353Sdim        // GNU extension.
299234353Sdim        return Ctx.LongLongTy;
300263508Sdim      case LengthModifier::None:
301263508Sdim        return Ctx.IntTy;
302263508Sdim      case LengthModifier::AsInt32:
303263508Sdim        return ArgType(Ctx.IntTy, "__int32");
304239462Sdim      case LengthModifier::AsChar: return ArgType::AnyCharTy;
305210299Sed      case LengthModifier::AsShort: return Ctx.ShortTy;
306210299Sed      case LengthModifier::AsLong: return Ctx.LongTy;
307234353Sdim      case LengthModifier::AsLongLong:
308234353Sdim      case LengthModifier::AsQuad:
309234353Sdim        return Ctx.LongLongTy;
310263508Sdim      case LengthModifier::AsInt64:
311263508Sdim        return ArgType(Ctx.LongLongTy, "__int64");
312210299Sed      case LengthModifier::AsIntMax:
313239462Sdim        return ArgType(Ctx.getIntMaxType(), "intmax_t");
314234353Sdim      case LengthModifier::AsSizeT:
315234353Sdim        // FIXME: How to get the corresponding signed version of size_t?
316239462Sdim        return ArgType();
317263508Sdim      case LengthModifier::AsInt3264:
318263508Sdim        return Ctx.getTargetInfo().getTriple().isArch64Bit()
319263508Sdim                   ? ArgType(Ctx.LongLongTy, "__int64")
320263508Sdim                   : ArgType(Ctx.IntTy, "__int32");
321234353Sdim      case LengthModifier::AsPtrDiff:
322239462Sdim        return ArgType(Ctx.getPointerDiffType(), "ptrdiff_t");
323234353Sdim      case LengthModifier::AsAllocate:
324234353Sdim      case LengthModifier::AsMAllocate:
325239462Sdim        return ArgType::Invalid();
326203955Srdivacky    }
327203955Srdivacky
328203955Srdivacky  if (CS.isUIntArg())
329210299Sed    switch (LM.getKind()) {
330210299Sed      case LengthModifier::AsLongDouble:
331234353Sdim        // GNU extension.
332234353Sdim        return Ctx.UnsignedLongLongTy;
333263508Sdim      case LengthModifier::None:
334263508Sdim        return Ctx.UnsignedIntTy;
335263508Sdim      case LengthModifier::AsInt32:
336263508Sdim        return ArgType(Ctx.UnsignedIntTy, "unsigned __int32");
337210299Sed      case LengthModifier::AsChar: return Ctx.UnsignedCharTy;
338210299Sed      case LengthModifier::AsShort: return Ctx.UnsignedShortTy;
339210299Sed      case LengthModifier::AsLong: return Ctx.UnsignedLongTy;
340234353Sdim      case LengthModifier::AsLongLong:
341234353Sdim      case LengthModifier::AsQuad:
342234353Sdim        return Ctx.UnsignedLongLongTy;
343263508Sdim      case LengthModifier::AsInt64:
344263508Sdim        return ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64");
345210299Sed      case LengthModifier::AsIntMax:
346239462Sdim        return ArgType(Ctx.getUIntMaxType(), "uintmax_t");
347210299Sed      case LengthModifier::AsSizeT:
348239462Sdim        return ArgType(Ctx.getSizeType(), "size_t");
349263508Sdim      case LengthModifier::AsInt3264:
350263508Sdim        return Ctx.getTargetInfo().getTriple().isArch64Bit()
351263508Sdim                   ? ArgType(Ctx.UnsignedLongLongTy, "unsigned __int64")
352263508Sdim                   : ArgType(Ctx.UnsignedIntTy, "unsigned __int32");
353210299Sed      case LengthModifier::AsPtrDiff:
354203955Srdivacky        // FIXME: How to get the corresponding unsigned
355203955Srdivacky        // version of ptrdiff_t?
356239462Sdim        return ArgType();
357234353Sdim      case LengthModifier::AsAllocate:
358234353Sdim      case LengthModifier::AsMAllocate:
359239462Sdim        return ArgType::Invalid();
360203955Srdivacky    }
361203955Srdivacky
362203955Srdivacky  if (CS.isDoubleArg()) {
363210299Sed    if (LM.getKind() == LengthModifier::AsLongDouble)
364203955Srdivacky      return Ctx.LongDoubleTy;
365203955Srdivacky    return Ctx.DoubleTy;
366203955Srdivacky  }
367203955Srdivacky
368239462Sdim  if (CS.getKind() == ConversionSpecifier::nArg) {
369239462Sdim    switch (LM.getKind()) {
370239462Sdim      case LengthModifier::None:
371239462Sdim        return ArgType::PtrTo(Ctx.IntTy);
372239462Sdim      case LengthModifier::AsChar:
373239462Sdim        return ArgType::PtrTo(Ctx.SignedCharTy);
374239462Sdim      case LengthModifier::AsShort:
375239462Sdim        return ArgType::PtrTo(Ctx.ShortTy);
376239462Sdim      case LengthModifier::AsLong:
377239462Sdim        return ArgType::PtrTo(Ctx.LongTy);
378239462Sdim      case LengthModifier::AsLongLong:
379239462Sdim      case LengthModifier::AsQuad:
380239462Sdim        return ArgType::PtrTo(Ctx.LongLongTy);
381239462Sdim      case LengthModifier::AsIntMax:
382239462Sdim        return ArgType::PtrTo(ArgType(Ctx.getIntMaxType(), "intmax_t"));
383239462Sdim      case LengthModifier::AsSizeT:
384239462Sdim        return ArgType(); // FIXME: ssize_t
385239462Sdim      case LengthModifier::AsPtrDiff:
386239462Sdim        return ArgType::PtrTo(ArgType(Ctx.getPointerDiffType(), "ptrdiff_t"));
387239462Sdim      case LengthModifier::AsLongDouble:
388239462Sdim        return ArgType(); // FIXME: Is this a known extension?
389239462Sdim      case LengthModifier::AsAllocate:
390239462Sdim      case LengthModifier::AsMAllocate:
391263508Sdim      case LengthModifier::AsInt32:
392263508Sdim      case LengthModifier::AsInt3264:
393263508Sdim      case LengthModifier::AsInt64:
394239462Sdim        return ArgType::Invalid();
395239462Sdim    }
396239462Sdim  }
397239462Sdim
398204643Srdivacky  switch (CS.getKind()) {
399212904Sdim    case ConversionSpecifier::sArg:
400234353Sdim      if (LM.getKind() == LengthModifier::AsWideChar) {
401234353Sdim        if (IsObjCLiteral)
402249423Sdim          return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()),
403249423Sdim                         "const unichar *");
404239462Sdim        return ArgType(ArgType::WCStrTy, "wchar_t *");
405234353Sdim      }
406239462Sdim      return ArgType::CStrTy;
407212904Sdim    case ConversionSpecifier::SArg:
408234353Sdim      if (IsObjCLiteral)
409249423Sdim        return ArgType(Ctx.getPointerType(Ctx.UnsignedShortTy.withConst()),
410249423Sdim                       "const unichar *");
411239462Sdim      return ArgType(ArgType::WCStrTy, "wchar_t *");
412204643Srdivacky    case ConversionSpecifier::CArg:
413234353Sdim      if (IsObjCLiteral)
414249423Sdim        return ArgType(Ctx.UnsignedShortTy, "unichar");
415263508Sdim      return ArgType(Ctx.WideCharTy, "wchar_t");
416212904Sdim    case ConversionSpecifier::pArg:
417239462Sdim      return ArgType::CPointerTy;
418234353Sdim    case ConversionSpecifier::ObjCObjArg:
419239462Sdim      return ArgType::ObjCPointerTy;
420204643Srdivacky    default:
421204643Srdivacky      break;
422204643Srdivacky  }
423203955Srdivacky
424203955Srdivacky  // FIXME: Handle other cases.
425239462Sdim  return ArgType();
426203955Srdivacky}
427203955Srdivacky
428234353Sdimbool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt,
429234353Sdim                              ASTContext &Ctx, bool IsObjCLiteral) {
430239462Sdim  // %n is different from other conversion specifiers; don't try to fix it.
431239462Sdim  if (CS.getKind() == ConversionSpecifier::nArg)
432239462Sdim    return false;
433239462Sdim
434239462Sdim  // Handle Objective-C objects first. Note that while the '%@' specifier will
435239462Sdim  // not warn for structure pointer or void pointer arguments (because that's
436239462Sdim  // how CoreFoundation objects are implemented), we only show a fixit for '%@'
437239462Sdim  // if we know it's an object (block, id, class, or __attribute__((NSObject))).
438239462Sdim  if (QT->isObjCRetainableType()) {
439239462Sdim    if (!IsObjCLiteral)
440239462Sdim      return false;
441239462Sdim
442239462Sdim    CS.setKind(ConversionSpecifier::ObjCObjArg);
443239462Sdim
444239462Sdim    // Disable irrelevant flags
445239462Sdim    HasThousandsGrouping = false;
446239462Sdim    HasPlusPrefix = false;
447239462Sdim    HasSpacePrefix = false;
448239462Sdim    HasAlternativeForm = false;
449239462Sdim    HasLeadingZeroes = false;
450239462Sdim    Precision.setHowSpecified(OptionalAmount::NotSpecified);
451239462Sdim    LM.setKind(LengthModifier::None);
452239462Sdim
453239462Sdim    return true;
454239462Sdim  }
455239462Sdim
456239462Sdim  // Handle strings next (char *, wchar_t *)
457210299Sed  if (QT->isPointerType() && (QT->getPointeeType()->isAnyCharacterType())) {
458212904Sdim    CS.setKind(ConversionSpecifier::sArg);
459210299Sed
460210299Sed    // Disable irrelevant flags
461210299Sed    HasAlternativeForm = 0;
462210299Sed    HasLeadingZeroes = 0;
463210299Sed
464210299Sed    // Set the long length modifier for wide characters
465210299Sed    if (QT->getPointeeType()->isWideCharType())
466210299Sed      LM.setKind(LengthModifier::AsWideChar);
467234353Sdim    else
468234353Sdim      LM.setKind(LengthModifier::None);
469210299Sed
470210299Sed    return true;
471210299Sed  }
472210299Sed
473239462Sdim  // If it's an enum, get its underlying type.
474239462Sdim  if (const EnumType *ETy = QT->getAs<EnumType>())
475239462Sdim    QT = ETy->getDecl()->getIntegerType();
476239462Sdim
477210299Sed  // We can only work with builtin types.
478234353Sdim  const BuiltinType *BT = QT->getAs<BuiltinType>();
479234353Sdim  if (!BT)
480210299Sed    return false;
481210299Sed
482210299Sed  // Set length modifier
483210299Sed  switch (BT->getKind()) {
484221345Sdim  case BuiltinType::Bool:
485221345Sdim  case BuiltinType::WChar_U:
486221345Sdim  case BuiltinType::WChar_S:
487221345Sdim  case BuiltinType::Char16:
488221345Sdim  case BuiltinType::Char32:
489221345Sdim  case BuiltinType::UInt128:
490221345Sdim  case BuiltinType::Int128:
491226633Sdim  case BuiltinType::Half:
492234353Sdim    // Various types which are non-trivial to correct.
493221345Sdim    return false;
494221345Sdim
495234353Sdim#define SIGNED_TYPE(Id, SingletonId)
496234353Sdim#define UNSIGNED_TYPE(Id, SingletonId)
497234353Sdim#define FLOATING_TYPE(Id, SingletonId)
498234353Sdim#define BUILTIN_TYPE(Id, SingletonId) \
499234353Sdim  case BuiltinType::Id:
500234353Sdim#include "clang/AST/BuiltinTypes.def"
501221345Sdim    // Misc other stuff which doesn't make sense here.
502221345Sdim    return false;
503221345Sdim
504221345Sdim  case BuiltinType::UInt:
505221345Sdim  case BuiltinType::Int:
506221345Sdim  case BuiltinType::Float:
507221345Sdim  case BuiltinType::Double:
508210299Sed    LM.setKind(LengthModifier::None);
509210299Sed    break;
510210299Sed
511218893Sdim  case BuiltinType::Char_U:
512218893Sdim  case BuiltinType::UChar:
513218893Sdim  case BuiltinType::Char_S:
514218893Sdim  case BuiltinType::SChar:
515218893Sdim    LM.setKind(LengthModifier::AsChar);
516218893Sdim    break;
517218893Sdim
518218893Sdim  case BuiltinType::Short:
519218893Sdim  case BuiltinType::UShort:
520218893Sdim    LM.setKind(LengthModifier::AsShort);
521218893Sdim    break;
522218893Sdim
523210299Sed  case BuiltinType::Long:
524210299Sed  case BuiltinType::ULong:
525210299Sed    LM.setKind(LengthModifier::AsLong);
526210299Sed    break;
527210299Sed
528210299Sed  case BuiltinType::LongLong:
529210299Sed  case BuiltinType::ULongLong:
530210299Sed    LM.setKind(LengthModifier::AsLongLong);
531210299Sed    break;
532210299Sed
533210299Sed  case BuiltinType::LongDouble:
534210299Sed    LM.setKind(LengthModifier::AsLongDouble);
535210299Sed    break;
536210299Sed  }
537210299Sed
538234353Sdim  // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99.
539249423Sdim  if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus11))
540239462Sdim    namedTypeToLengthModifier(QT, LM);
541234353Sdim
542249423Sdim  // If fixing the length modifier was enough, we might be done.
543243830Sdim  if (hasValidLengthModifier(Ctx.getTargetInfo())) {
544249423Sdim    // If we're going to offer a fix anyway, make sure the sign matches.
545249423Sdim    switch (CS.getKind()) {
546249423Sdim    case ConversionSpecifier::uArg:
547249423Sdim    case ConversionSpecifier::UArg:
548249423Sdim      if (QT->isSignedIntegerType())
549249423Sdim        CS.setKind(clang::analyze_format_string::ConversionSpecifier::dArg);
550249423Sdim      break;
551249423Sdim    case ConversionSpecifier::dArg:
552249423Sdim    case ConversionSpecifier::DArg:
553249423Sdim    case ConversionSpecifier::iArg:
554249423Sdim      if (QT->isUnsignedIntegerType() && !HasPlusPrefix)
555249423Sdim        CS.setKind(clang::analyze_format_string::ConversionSpecifier::uArg);
556249423Sdim      break;
557249423Sdim    default:
558249423Sdim      // Other specifiers do not have signed/unsigned variants.
559249423Sdim      break;
560249423Sdim    }
561249423Sdim
562243830Sdim    const analyze_printf::ArgType &ATR = getArgType(Ctx, IsObjCLiteral);
563243830Sdim    if (ATR.isValid() && ATR.matchesType(Ctx, QT))
564243830Sdim      return true;
565243830Sdim  }
566234353Sdim
567210299Sed  // Set conversion specifier and disable any flags which do not apply to it.
568218893Sdim  // Let typedefs to char fall through to int, as %c is silly for uint8_t.
569249423Sdim  if (!isa<TypedefType>(QT) && QT->isCharType()) {
570212904Sdim    CS.setKind(ConversionSpecifier::cArg);
571218893Sdim    LM.setKind(LengthModifier::None);
572210299Sed    Precision.setHowSpecified(OptionalAmount::NotSpecified);
573210299Sed    HasAlternativeForm = 0;
574210299Sed    HasLeadingZeroes = 0;
575210299Sed    HasPlusPrefix = 0;
576210299Sed  }
577210299Sed  // Test for Floating type first as LongDouble can pass isUnsignedIntegerType
578210299Sed  else if (QT->isRealFloatingType()) {
579210299Sed    CS.setKind(ConversionSpecifier::fArg);
580210299Sed  }
581210299Sed  else if (QT->isSignedIntegerType()) {
582210299Sed    CS.setKind(ConversionSpecifier::dArg);
583210299Sed    HasAlternativeForm = 0;
584210299Sed  }
585210299Sed  else if (QT->isUnsignedIntegerType()) {
586234353Sdim    CS.setKind(ConversionSpecifier::uArg);
587210299Sed    HasAlternativeForm = 0;
588210299Sed    HasPlusPrefix = 0;
589226633Sdim  } else {
590226633Sdim    llvm_unreachable("Unexpected type");
591210299Sed  }
592210299Sed
593210299Sed  return true;
594210299Sed}
595210299Sed
596226633Sdimvoid PrintfSpecifier::toString(raw_ostream &os) const {
597210299Sed  // Whilst some features have no defined order, we are using the order
598218893Sdim  // appearing in the C99 standard (ISO/IEC 9899:1999 (E) 7.19.6.1)
599210299Sed  os << "%";
600210299Sed
601210299Sed  // Positional args
602210299Sed  if (usesPositionalArg()) {
603210299Sed    os << getPositionalArgIndex() << "$";
604210299Sed  }
605210299Sed
606210299Sed  // Conversion flags
607210299Sed  if (IsLeftJustified)    os << "-";
608210299Sed  if (HasPlusPrefix)      os << "+";
609210299Sed  if (HasSpacePrefix)     os << " ";
610210299Sed  if (HasAlternativeForm) os << "#";
611210299Sed  if (HasLeadingZeroes)   os << "0";
612210299Sed
613210299Sed  // Minimum field width
614210299Sed  FieldWidth.toString(os);
615210299Sed  // Precision
616210299Sed  Precision.toString(os);
617210299Sed  // Length modifier
618210299Sed  os << LM.toString();
619210299Sed  // Conversion specifier
620210299Sed  os << CS.toString();
621210299Sed}
622210299Sed
623212904Sdimbool PrintfSpecifier::hasValidPlusPrefix() const {
624210299Sed  if (!HasPlusPrefix)
625210299Sed    return true;
626210299Sed
627210299Sed  // The plus prefix only makes sense for signed conversions
628210299Sed  switch (CS.getKind()) {
629210299Sed  case ConversionSpecifier::dArg:
630243830Sdim  case ConversionSpecifier::DArg:
631210299Sed  case ConversionSpecifier::iArg:
632210299Sed  case ConversionSpecifier::fArg:
633210299Sed  case ConversionSpecifier::FArg:
634210299Sed  case ConversionSpecifier::eArg:
635210299Sed  case ConversionSpecifier::EArg:
636210299Sed  case ConversionSpecifier::gArg:
637210299Sed  case ConversionSpecifier::GArg:
638210299Sed  case ConversionSpecifier::aArg:
639210299Sed  case ConversionSpecifier::AArg:
640243830Sdim  case ConversionSpecifier::FreeBSDrArg:
641210299Sed    return true;
642210299Sed
643210299Sed  default:
644210299Sed    return false;
645210299Sed  }
646210299Sed}
647210299Sed
648212904Sdimbool PrintfSpecifier::hasValidAlternativeForm() const {
649210299Sed  if (!HasAlternativeForm)
650210299Sed    return true;
651210299Sed
652218893Sdim  // Alternate form flag only valid with the oxXaAeEfFgG conversions
653210299Sed  switch (CS.getKind()) {
654210299Sed  case ConversionSpecifier::oArg:
655243830Sdim  case ConversionSpecifier::OArg:
656210299Sed  case ConversionSpecifier::xArg:
657218893Sdim  case ConversionSpecifier::XArg:
658210299Sed  case ConversionSpecifier::aArg:
659210299Sed  case ConversionSpecifier::AArg:
660210299Sed  case ConversionSpecifier::eArg:
661210299Sed  case ConversionSpecifier::EArg:
662210299Sed  case ConversionSpecifier::fArg:
663210299Sed  case ConversionSpecifier::FArg:
664210299Sed  case ConversionSpecifier::gArg:
665210299Sed  case ConversionSpecifier::GArg:
666243830Sdim  case ConversionSpecifier::FreeBSDrArg:
667210299Sed    return true;
668210299Sed
669210299Sed  default:
670210299Sed    return false;
671210299Sed  }
672210299Sed}
673210299Sed
674212904Sdimbool PrintfSpecifier::hasValidLeadingZeros() const {
675210299Sed  if (!HasLeadingZeroes)
676210299Sed    return true;
677210299Sed
678210299Sed  // Leading zeroes flag only valid with the diouxXaAeEfFgG conversions
679210299Sed  switch (CS.getKind()) {
680210299Sed  case ConversionSpecifier::dArg:
681243830Sdim  case ConversionSpecifier::DArg:
682210299Sed  case ConversionSpecifier::iArg:
683210299Sed  case ConversionSpecifier::oArg:
684243830Sdim  case ConversionSpecifier::OArg:
685210299Sed  case ConversionSpecifier::uArg:
686243830Sdim  case ConversionSpecifier::UArg:
687210299Sed  case ConversionSpecifier::xArg:
688210299Sed  case ConversionSpecifier::XArg:
689210299Sed  case ConversionSpecifier::aArg:
690210299Sed  case ConversionSpecifier::AArg:
691210299Sed  case ConversionSpecifier::eArg:
692210299Sed  case ConversionSpecifier::EArg:
693210299Sed  case ConversionSpecifier::fArg:
694210299Sed  case ConversionSpecifier::FArg:
695210299Sed  case ConversionSpecifier::gArg:
696210299Sed  case ConversionSpecifier::GArg:
697210299Sed    return true;
698210299Sed
699210299Sed  default:
700210299Sed    return false;
701210299Sed  }
702210299Sed}
703210299Sed
704212904Sdimbool PrintfSpecifier::hasValidSpacePrefix() const {
705210299Sed  if (!HasSpacePrefix)
706210299Sed    return true;
707210299Sed
708210299Sed  // The space prefix only makes sense for signed conversions
709210299Sed  switch (CS.getKind()) {
710210299Sed  case ConversionSpecifier::dArg:
711243830Sdim  case ConversionSpecifier::DArg:
712210299Sed  case ConversionSpecifier::iArg:
713210299Sed  case ConversionSpecifier::fArg:
714210299Sed  case ConversionSpecifier::FArg:
715210299Sed  case ConversionSpecifier::eArg:
716210299Sed  case ConversionSpecifier::EArg:
717210299Sed  case ConversionSpecifier::gArg:
718210299Sed  case ConversionSpecifier::GArg:
719210299Sed  case ConversionSpecifier::aArg:
720210299Sed  case ConversionSpecifier::AArg:
721210299Sed    return true;
722210299Sed
723210299Sed  default:
724210299Sed    return false;
725210299Sed  }
726210299Sed}
727210299Sed
728212904Sdimbool PrintfSpecifier::hasValidLeftJustified() const {
729210299Sed  if (!IsLeftJustified)
730210299Sed    return true;
731210299Sed
732210299Sed  // The left justified flag is valid for all conversions except n
733210299Sed  switch (CS.getKind()) {
734212904Sdim  case ConversionSpecifier::nArg:
735210299Sed    return false;
736210299Sed
737210299Sed  default:
738210299Sed    return true;
739210299Sed  }
740210299Sed}
741210299Sed
742218893Sdimbool PrintfSpecifier::hasValidThousandsGroupingPrefix() const {
743218893Sdim  if (!HasThousandsGrouping)
744218893Sdim    return true;
745218893Sdim
746218893Sdim  switch (CS.getKind()) {
747218893Sdim    case ConversionSpecifier::dArg:
748243830Sdim    case ConversionSpecifier::DArg:
749218893Sdim    case ConversionSpecifier::iArg:
750218893Sdim    case ConversionSpecifier::uArg:
751243830Sdim    case ConversionSpecifier::UArg:
752218893Sdim    case ConversionSpecifier::fArg:
753218893Sdim    case ConversionSpecifier::FArg:
754218893Sdim    case ConversionSpecifier::gArg:
755218893Sdim    case ConversionSpecifier::GArg:
756218893Sdim      return true;
757218893Sdim    default:
758218893Sdim      return false;
759218893Sdim  }
760218893Sdim}
761218893Sdim
762212904Sdimbool PrintfSpecifier::hasValidPrecision() const {
763210299Sed  if (Precision.getHowSpecified() == OptionalAmount::NotSpecified)
764210299Sed    return true;
765210299Sed
766210299Sed  // Precision is only valid with the diouxXaAeEfFgGs conversions
767210299Sed  switch (CS.getKind()) {
768210299Sed  case ConversionSpecifier::dArg:
769243830Sdim  case ConversionSpecifier::DArg:
770210299Sed  case ConversionSpecifier::iArg:
771210299Sed  case ConversionSpecifier::oArg:
772243830Sdim  case ConversionSpecifier::OArg:
773210299Sed  case ConversionSpecifier::uArg:
774243830Sdim  case ConversionSpecifier::UArg:
775210299Sed  case ConversionSpecifier::xArg:
776210299Sed  case ConversionSpecifier::XArg:
777210299Sed  case ConversionSpecifier::aArg:
778210299Sed  case ConversionSpecifier::AArg:
779210299Sed  case ConversionSpecifier::eArg:
780210299Sed  case ConversionSpecifier::EArg:
781210299Sed  case ConversionSpecifier::fArg:
782210299Sed  case ConversionSpecifier::FArg:
783210299Sed  case ConversionSpecifier::gArg:
784210299Sed  case ConversionSpecifier::GArg:
785212904Sdim  case ConversionSpecifier::sArg:
786210299Sed    return true;
787210299Sed
788210299Sed  default:
789210299Sed    return false;
790210299Sed  }
791210299Sed}
792212904Sdimbool PrintfSpecifier::hasValidFieldWidth() const {
793210299Sed  if (FieldWidth.getHowSpecified() == OptionalAmount::NotSpecified)
794210299Sed      return true;
795210299Sed
796210299Sed  // The field width is valid for all conversions except n
797210299Sed  switch (CS.getKind()) {
798212904Sdim  case ConversionSpecifier::nArg:
799210299Sed    return false;
800210299Sed
801210299Sed  default:
802210299Sed    return true;
803210299Sed  }
804210299Sed}
805