PrintfFormatString.cpp revision 377cc6e3e8a6d9d0c90a315d29ce84836383d059
1//== PrintfFormatString.cpp - Analysis of printf format strings --*- C++ -*-==// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// Handling of format string in printf and friends. The structure of format 11// strings for fprintf() are described in C99 7.19.6.1. 12// 13//===----------------------------------------------------------------------===// 14 15#include "clang/Analysis/Analyses/FormatString.h" 16#include "clang/Sema/Sema.h" 17#include "FormatStringParsing.h" 18 19using clang::analyze_format_string::ArgTypeResult; 20using clang::analyze_format_string::FormatStringHandler; 21using clang::analyze_format_string::LengthModifier; 22using clang::analyze_format_string::OptionalAmount; 23using clang::analyze_format_string::ConversionSpecifier; 24using clang::analyze_printf::PrintfSpecifier; 25 26using namespace clang; 27 28typedef clang::analyze_format_string::SpecifierResult<PrintfSpecifier> 29 PrintfSpecifierResult; 30 31//===----------------------------------------------------------------------===// 32// Methods for parsing format strings. 33//===----------------------------------------------------------------------===// 34 35using analyze_format_string::ParseNonPositionAmount; 36 37static bool ParsePrecision(FormatStringHandler &H, PrintfSpecifier &FS, 38 const char *Start, const char *&Beg, const char *E, 39 unsigned *argIndex) { 40 if (argIndex) { 41 FS.setPrecision(ParseNonPositionAmount(Beg, E, *argIndex)); 42 } else { 43 const OptionalAmount Amt = ParsePositionAmount(H, Start, Beg, E, 44 analyze_format_string::PrecisionPos); 45 if (Amt.isInvalid()) 46 return true; 47 FS.setPrecision(Amt); 48 } 49 return false; 50} 51 52static PrintfSpecifierResult ParsePrintfSpecifier(FormatStringHandler &H, 53 const char *&Beg, 54 const char *E, 55 unsigned &argIndex) { 56 57 using namespace clang::analyze_format_string; 58 using namespace clang::analyze_printf; 59 60 const char *I = Beg; 61 const char *Start = 0; 62 UpdateOnReturn <const char*> UpdateBeg(Beg, I); 63 64 // Look for a '%' character that indicates the start of a format specifier. 65 for ( ; I != E ; ++I) { 66 char c = *I; 67 if (c == '\0') { 68 // Detect spurious null characters, which are likely errors. 69 H.HandleNullChar(I); 70 return true; 71 } 72 if (c == '%') { 73 Start = I++; // Record the start of the format specifier. 74 break; 75 } 76 } 77 78 // No format specifier found? 79 if (!Start) 80 return false; 81 82 if (I == E) { 83 // No more characters left? 84 H.HandleIncompleteSpecifier(Start, E - Start); 85 return true; 86 } 87 88 PrintfSpecifier FS; 89 if (ParseArgPosition(H, FS, Start, I, E)) 90 return true; 91 92 if (I == E) { 93 // No more characters left? 94 H.HandleIncompleteSpecifier(Start, E - Start); 95 return true; 96 } 97 98 // Look for flags (if any). 99 bool hasMore = true; 100 for ( ; I != E; ++I) { 101 switch (*I) { 102 default: hasMore = false; break; 103 case '\'': 104 // FIXME: POSIX specific. Always accept? 105 FS.setHasThousandsGrouping(I); 106 break; 107 case '-': FS.setIsLeftJustified(I); break; 108 case '+': FS.setHasPlusPrefix(I); break; 109 case ' ': FS.setHasSpacePrefix(I); break; 110 case '#': FS.setHasAlternativeForm(I); break; 111 case '0': FS.setHasLeadingZeros(I); break; 112 } 113 if (!hasMore) 114 break; 115 } 116 117 if (I == E) { 118 // No more characters left? 119 H.HandleIncompleteSpecifier(Start, E - Start); 120 return true; 121 } 122 123 // Look for the field width (if any). 124 if (ParseFieldWidth(H, FS, Start, I, E, 125 FS.usesPositionalArg() ? 0 : &argIndex)) 126 return true; 127 128 if (I == E) { 129 // No more characters left? 130 H.HandleIncompleteSpecifier(Start, E - Start); 131 return true; 132 } 133 134 // Look for the precision (if any). 135 if (*I == '.') { 136 ++I; 137 if (I == E) { 138 H.HandleIncompleteSpecifier(Start, E - Start); 139 return true; 140 } 141 142 if (ParsePrecision(H, FS, Start, I, E, 143 FS.usesPositionalArg() ? 0 : &argIndex)) 144 return true; 145 146 if (I == E) { 147 // No more characters left? 148 H.HandleIncompleteSpecifier(Start, E - Start); 149 return true; 150 } 151 } 152 153 // Look for the length modifier. 154 if (ParseLengthModifier(FS, I, E) && I == E) { 155 // No more characters left? 156 H.HandleIncompleteSpecifier(Start, E - Start); 157 return true; 158 } 159 160 if (*I == '\0') { 161 // Detect spurious null characters, which are likely errors. 162 H.HandleNullChar(I); 163 return true; 164 } 165 166 // Finally, look for the conversion specifier. 167 const char *conversionPosition = I++; 168 ConversionSpecifier::Kind k = ConversionSpecifier::InvalidSpecifier; 169 switch (*conversionPosition) { 170 default: 171 break; 172 // C99: 7.19.6.1 (section 8). 173 case '%': k = ConversionSpecifier::PercentArg; break; 174 case 'A': k = ConversionSpecifier::AArg; break; 175 case 'E': k = ConversionSpecifier::EArg; break; 176 case 'F': k = ConversionSpecifier::FArg; break; 177 case 'G': k = ConversionSpecifier::GArg; break; 178 case 'X': k = ConversionSpecifier::XArg; break; 179 case 'a': k = ConversionSpecifier::aArg; break; 180 case 'c': k = ConversionSpecifier::cArg; break; 181 case 'd': k = ConversionSpecifier::dArg; break; 182 case 'e': k = ConversionSpecifier::eArg; break; 183 case 'f': k = ConversionSpecifier::fArg; break; 184 case 'g': k = ConversionSpecifier::gArg; break; 185 case 'i': k = ConversionSpecifier::iArg; break; 186 case 'n': k = ConversionSpecifier::nArg; break; 187 case 'o': k = ConversionSpecifier::oArg; break; 188 case 'p': k = ConversionSpecifier::pArg; break; 189 case 's': k = ConversionSpecifier::sArg; break; 190 case 'u': k = ConversionSpecifier::uArg; break; 191 case 'x': k = ConversionSpecifier::xArg; break; 192 // POSIX specific. 193 case 'C': k = ConversionSpecifier::CArg; break; 194 case 'S': k = ConversionSpecifier::SArg; break; 195 // Objective-C. 196 case '@': k = ConversionSpecifier::ObjCObjArg; break; 197 // Glibc specific. 198 case 'm': k = ConversionSpecifier::PrintErrno; break; 199 } 200 PrintfConversionSpecifier CS(conversionPosition, k); 201 FS.setConversionSpecifier(CS); 202 if (CS.consumesDataArgument() && !FS.usesPositionalArg()) 203 FS.setArgIndex(argIndex++); 204 205 if (k == ConversionSpecifier::InvalidSpecifier) { 206 // Assume the conversion takes one argument. 207 return !H.HandleInvalidPrintfConversionSpecifier(FS, Start, I - Start); 208 } 209 return PrintfSpecifierResult(Start, FS); 210} 211 212bool clang::analyze_format_string::ParsePrintfString(FormatStringHandler &H, 213 const char *I, 214 const char *E) { 215 216 unsigned argIndex = 0; 217 218 // Keep looking for a format specifier until we have exhausted the string. 219 while (I != E) { 220 const PrintfSpecifierResult &FSR = ParsePrintfSpecifier(H, I, E, argIndex); 221 // Did a fail-stop error of any kind occur when parsing the specifier? 222 // If so, don't do any more processing. 223 if (FSR.shouldStop()) 224 return true;; 225 // Did we exhaust the string or encounter an error that 226 // we can recover from? 227 if (!FSR.hasValue()) 228 continue; 229 // We have a format specifier. Pass it to the callback. 230 if (!H.HandlePrintfSpecifier(FSR.getValue(), FSR.getStart(), 231 I - FSR.getStart())) 232 return true; 233 } 234 assert(I == E && "Format string not exhausted"); 235 return false; 236} 237 238//===----------------------------------------------------------------------===// 239// Methods on ConversionSpecifier. 240//===----------------------------------------------------------------------===// 241const char *ConversionSpecifier::toString() const { 242 switch (kind) { 243 case dArg: return "d"; 244 case iArg: return "i"; 245 case oArg: return "o"; 246 case uArg: return "u"; 247 case xArg: return "x"; 248 case XArg: return "X"; 249 case fArg: return "f"; 250 case FArg: return "F"; 251 case eArg: return "e"; 252 case EArg: return "E"; 253 case gArg: return "g"; 254 case GArg: return "G"; 255 case aArg: return "a"; 256 case AArg: return "A"; 257 case cArg: return "c"; 258 case sArg: return "s"; 259 case pArg: return "p"; 260 case nArg: return "n"; 261 case PercentArg: return "%"; 262 case ScanListArg: return "["; 263 case InvalidSpecifier: return NULL; 264 265 // MacOS X unicode extensions. 266 case CArg: return "C"; 267 case SArg: return "S"; 268 269 // Objective-C specific specifiers. 270 case ObjCObjArg: return "@"; 271 272 // GlibC specific specifiers. 273 case PrintErrno: return "m"; 274 } 275 return NULL; 276} 277 278//===----------------------------------------------------------------------===// 279// Methods on PrintfSpecifier. 280//===----------------------------------------------------------------------===// 281 282/// \brief Try to find and return a typedef type named Name whose actual type 283/// is Underlying. Return Underlying if such a typedef cannot be found. 284static QualType FindTypedef(Sema &S, const char *Name, QualType Underlying) { 285 ASTContext &Ctx = S.getASTContext(); 286 IdentifierInfo &II = Ctx.Idents.get(Name); 287 288 NamedDecl *D = S.LookupSingleName(S.getCurScope(), DeclarationName(&II), 289 SourceLocation(), Sema::LookupOrdinaryName); 290 291 if (TypedefDecl *TD = dyn_cast_or_null<TypedefDecl>(D)) { 292 QualType TypedefType = Ctx.getTypedefType(TD, QualType()); 293 if (Ctx.getCanonicalType(TypedefType) == Underlying) 294 return TypedefType; 295 } 296 297 return Underlying; 298} 299 300ArgTypeResult PrintfSpecifier::getArgType(Sema &S) const { 301 const PrintfConversionSpecifier &CS = getConversionSpecifier(); 302 ASTContext &Ctx = S.getASTContext(); 303 304 if (!CS.consumesDataArgument()) 305 return ArgTypeResult::Invalid(); 306 307 if (CS.getKind() == ConversionSpecifier::cArg) 308 switch (LM.getKind()) { 309 case LengthModifier::None: return Ctx.IntTy; 310 case LengthModifier::AsLong: return ArgTypeResult::WIntTy; 311 default: 312 return ArgTypeResult::Invalid(); 313 } 314 315 if (CS.isIntArg()) 316 switch (LM.getKind()) { 317 case LengthModifier::AsLongDouble: 318 return ArgTypeResult::Invalid(); 319 case LengthModifier::None: return Ctx.IntTy; 320 case LengthModifier::AsChar: return ArgTypeResult::AnyCharTy; 321 case LengthModifier::AsShort: return Ctx.ShortTy; 322 case LengthModifier::AsLong: return Ctx.LongTy; 323 case LengthModifier::AsLongLong: return Ctx.LongLongTy; 324 case LengthModifier::AsIntMax: 325 return FindTypedef(S, "intmax_t", Ctx.getIntMaxType()); 326 case LengthModifier::AsSizeT: 327 // FIXME: How to get the corresponding signed version of size_t? 328 return ArgTypeResult(); 329 case LengthModifier::AsPtrDiff: 330 return FindTypedef(S, "ptrdiff_t", Ctx.getPointerDiffType()); 331 } 332 333 if (CS.isUIntArg()) 334 switch (LM.getKind()) { 335 case LengthModifier::AsLongDouble: 336 return ArgTypeResult::Invalid(); 337 case LengthModifier::None: return Ctx.UnsignedIntTy; 338 case LengthModifier::AsChar: return Ctx.UnsignedCharTy; 339 case LengthModifier::AsShort: return Ctx.UnsignedShortTy; 340 case LengthModifier::AsLong: return Ctx.UnsignedLongTy; 341 case LengthModifier::AsLongLong: return Ctx.UnsignedLongLongTy; 342 case LengthModifier::AsIntMax: 343 return FindTypedef(S, "uintmax_t", Ctx.getUIntMaxType()); 344 case LengthModifier::AsSizeT: 345 return FindTypedef(S, "size_t", Ctx.getSizeType()); 346 case LengthModifier::AsPtrDiff: 347 // FIXME: How to get the corresponding unsigned 348 // version of ptrdiff_t? 349 return ArgTypeResult(); 350 } 351 352 if (CS.isDoubleArg()) { 353 if (LM.getKind() == LengthModifier::AsLongDouble) 354 return Ctx.LongDoubleTy; 355 return Ctx.DoubleTy; 356 } 357 358 switch (CS.getKind()) { 359 case ConversionSpecifier::sArg: 360 return ArgTypeResult(LM.getKind() == LengthModifier::AsWideChar ? 361 ArgTypeResult::WCStrTy : ArgTypeResult::CStrTy); 362 case ConversionSpecifier::SArg: 363 // FIXME: This appears to be Mac OS X specific. 364 return ArgTypeResult::WCStrTy; 365 case ConversionSpecifier::CArg: 366 return Ctx.WCharTy; 367 case ConversionSpecifier::pArg: 368 return ArgTypeResult::CPointerTy; 369 default: 370 break; 371 } 372 373 // FIXME: Handle other cases. 374 return ArgTypeResult(); 375} 376 377bool PrintfSpecifier::fixType(QualType QT, const LangOptions &LangOpt) { 378 // Handle strings first (char *, wchar_t *) 379 if (QT->isPointerType() && (QT->getPointeeType()->isAnyCharacterType())) { 380 CS.setKind(ConversionSpecifier::sArg); 381 382 // Disable irrelevant flags 383 HasAlternativeForm = 0; 384 HasLeadingZeroes = 0; 385 386 // Set the long length modifier for wide characters 387 if (QT->getPointeeType()->isWideCharType()) 388 LM.setKind(LengthModifier::AsWideChar); 389 390 return true; 391 } 392 393 // We can only work with builtin types. 394 const BuiltinType *BT = QT->getAs<BuiltinType>(); 395 if (!BT) 396 return false; 397 398 // Set length modifier 399 switch (BT->getKind()) { 400 case BuiltinType::Bool: 401 case BuiltinType::WChar_U: 402 case BuiltinType::WChar_S: 403 case BuiltinType::Char16: 404 case BuiltinType::Char32: 405 case BuiltinType::UInt128: 406 case BuiltinType::Int128: 407 case BuiltinType::Half: 408 // Various types which are non-trivial to correct. 409 return false; 410 411#define SIGNED_TYPE(Id, SingletonId) 412#define UNSIGNED_TYPE(Id, SingletonId) 413#define FLOATING_TYPE(Id, SingletonId) 414#define BUILTIN_TYPE(Id, SingletonId) \ 415 case BuiltinType::Id: 416#include "clang/AST/BuiltinTypes.def" 417 // Misc other stuff which doesn't make sense here. 418 return false; 419 420 case BuiltinType::UInt: 421 case BuiltinType::Int: 422 case BuiltinType::Float: 423 case BuiltinType::Double: 424 LM.setKind(LengthModifier::None); 425 break; 426 427 case BuiltinType::Char_U: 428 case BuiltinType::UChar: 429 case BuiltinType::Char_S: 430 case BuiltinType::SChar: 431 LM.setKind(LengthModifier::AsChar); 432 break; 433 434 case BuiltinType::Short: 435 case BuiltinType::UShort: 436 LM.setKind(LengthModifier::AsShort); 437 break; 438 439 case BuiltinType::Long: 440 case BuiltinType::ULong: 441 LM.setKind(LengthModifier::AsLong); 442 break; 443 444 case BuiltinType::LongLong: 445 case BuiltinType::ULongLong: 446 LM.setKind(LengthModifier::AsLongLong); 447 break; 448 449 case BuiltinType::LongDouble: 450 LM.setKind(LengthModifier::AsLongDouble); 451 break; 452 } 453 454 // Handle size_t, ptrdiff_t, etc. that have dedicated length modifiers in C99. 455 if (isa<TypedefType>(QT) && (LangOpt.C99 || LangOpt.CPlusPlus0x)) { 456 const IdentifierInfo *Identifier = QT.getBaseTypeIdentifier(); 457 if (Identifier->getName() == "size_t") { 458 LM.setKind(LengthModifier::AsSizeT); 459 } else if (Identifier->getName() == "ssize_t") { 460 // Not C99, but common in Unix. 461 LM.setKind(LengthModifier::AsSizeT); 462 } else if (Identifier->getName() == "intmax_t") { 463 LM.setKind(LengthModifier::AsIntMax); 464 } else if (Identifier->getName() == "uintmax_t") { 465 LM.setKind(LengthModifier::AsIntMax); 466 } else if (Identifier->getName() == "ptrdiff_t") { 467 LM.setKind(LengthModifier::AsPtrDiff); 468 } 469 } 470 471 // Set conversion specifier and disable any flags which do not apply to it. 472 // Let typedefs to char fall through to int, as %c is silly for uint8_t. 473 if (isa<TypedefType>(QT) && QT->isAnyCharacterType()) { 474 CS.setKind(ConversionSpecifier::cArg); 475 LM.setKind(LengthModifier::None); 476 Precision.setHowSpecified(OptionalAmount::NotSpecified); 477 HasAlternativeForm = 0; 478 HasLeadingZeroes = 0; 479 HasPlusPrefix = 0; 480 } 481 // Test for Floating type first as LongDouble can pass isUnsignedIntegerType 482 else if (QT->isRealFloatingType()) { 483 CS.setKind(ConversionSpecifier::fArg); 484 } 485 else if (QT->isSignedIntegerType()) { 486 CS.setKind(ConversionSpecifier::dArg); 487 HasAlternativeForm = 0; 488 } 489 else if (QT->isUnsignedIntegerType()) { 490 // Preserve the original formatting, e.g. 'X', 'o'. 491 if (!cast<PrintfConversionSpecifier>(CS).isUIntArg()) 492 CS.setKind(ConversionSpecifier::uArg); 493 HasAlternativeForm = 0; 494 HasPlusPrefix = 0; 495 } else { 496 llvm_unreachable("Unexpected type"); 497 } 498 499 return true; 500} 501 502void PrintfSpecifier::toString(raw_ostream &os) const { 503 // Whilst some features have no defined order, we are using the order 504 // appearing in the C99 standard (ISO/IEC 9899:1999 (E) 7.19.6.1) 505 os << "%"; 506 507 // Positional args 508 if (usesPositionalArg()) { 509 os << getPositionalArgIndex() << "$"; 510 } 511 512 // Conversion flags 513 if (IsLeftJustified) os << "-"; 514 if (HasPlusPrefix) os << "+"; 515 if (HasSpacePrefix) os << " "; 516 if (HasAlternativeForm) os << "#"; 517 if (HasLeadingZeroes) os << "0"; 518 519 // Minimum field width 520 FieldWidth.toString(os); 521 // Precision 522 Precision.toString(os); 523 // Length modifier 524 os << LM.toString(); 525 // Conversion specifier 526 os << CS.toString(); 527} 528 529bool PrintfSpecifier::hasValidPlusPrefix() const { 530 if (!HasPlusPrefix) 531 return true; 532 533 // The plus prefix only makes sense for signed conversions 534 switch (CS.getKind()) { 535 case ConversionSpecifier::dArg: 536 case ConversionSpecifier::iArg: 537 case ConversionSpecifier::fArg: 538 case ConversionSpecifier::FArg: 539 case ConversionSpecifier::eArg: 540 case ConversionSpecifier::EArg: 541 case ConversionSpecifier::gArg: 542 case ConversionSpecifier::GArg: 543 case ConversionSpecifier::aArg: 544 case ConversionSpecifier::AArg: 545 return true; 546 547 default: 548 return false; 549 } 550} 551 552bool PrintfSpecifier::hasValidAlternativeForm() const { 553 if (!HasAlternativeForm) 554 return true; 555 556 // Alternate form flag only valid with the oxXaAeEfFgG conversions 557 switch (CS.getKind()) { 558 case ConversionSpecifier::oArg: 559 case ConversionSpecifier::xArg: 560 case ConversionSpecifier::XArg: 561 case ConversionSpecifier::aArg: 562 case ConversionSpecifier::AArg: 563 case ConversionSpecifier::eArg: 564 case ConversionSpecifier::EArg: 565 case ConversionSpecifier::fArg: 566 case ConversionSpecifier::FArg: 567 case ConversionSpecifier::gArg: 568 case ConversionSpecifier::GArg: 569 return true; 570 571 default: 572 return false; 573 } 574} 575 576bool PrintfSpecifier::hasValidLeadingZeros() const { 577 if (!HasLeadingZeroes) 578 return true; 579 580 // Leading zeroes flag only valid with the diouxXaAeEfFgG conversions 581 switch (CS.getKind()) { 582 case ConversionSpecifier::dArg: 583 case ConversionSpecifier::iArg: 584 case ConversionSpecifier::oArg: 585 case ConversionSpecifier::uArg: 586 case ConversionSpecifier::xArg: 587 case ConversionSpecifier::XArg: 588 case ConversionSpecifier::aArg: 589 case ConversionSpecifier::AArg: 590 case ConversionSpecifier::eArg: 591 case ConversionSpecifier::EArg: 592 case ConversionSpecifier::fArg: 593 case ConversionSpecifier::FArg: 594 case ConversionSpecifier::gArg: 595 case ConversionSpecifier::GArg: 596 return true; 597 598 default: 599 return false; 600 } 601} 602 603bool PrintfSpecifier::hasValidSpacePrefix() const { 604 if (!HasSpacePrefix) 605 return true; 606 607 // The space prefix only makes sense for signed conversions 608 switch (CS.getKind()) { 609 case ConversionSpecifier::dArg: 610 case ConversionSpecifier::iArg: 611 case ConversionSpecifier::fArg: 612 case ConversionSpecifier::FArg: 613 case ConversionSpecifier::eArg: 614 case ConversionSpecifier::EArg: 615 case ConversionSpecifier::gArg: 616 case ConversionSpecifier::GArg: 617 case ConversionSpecifier::aArg: 618 case ConversionSpecifier::AArg: 619 return true; 620 621 default: 622 return false; 623 } 624} 625 626bool PrintfSpecifier::hasValidLeftJustified() const { 627 if (!IsLeftJustified) 628 return true; 629 630 // The left justified flag is valid for all conversions except n 631 switch (CS.getKind()) { 632 case ConversionSpecifier::nArg: 633 return false; 634 635 default: 636 return true; 637 } 638} 639 640bool PrintfSpecifier::hasValidThousandsGroupingPrefix() const { 641 if (!HasThousandsGrouping) 642 return true; 643 644 switch (CS.getKind()) { 645 case ConversionSpecifier::dArg: 646 case ConversionSpecifier::iArg: 647 case ConversionSpecifier::uArg: 648 case ConversionSpecifier::fArg: 649 case ConversionSpecifier::FArg: 650 case ConversionSpecifier::gArg: 651 case ConversionSpecifier::GArg: 652 return true; 653 default: 654 return false; 655 } 656} 657 658bool PrintfSpecifier::hasValidPrecision() const { 659 if (Precision.getHowSpecified() == OptionalAmount::NotSpecified) 660 return true; 661 662 // Precision is only valid with the diouxXaAeEfFgGs conversions 663 switch (CS.getKind()) { 664 case ConversionSpecifier::dArg: 665 case ConversionSpecifier::iArg: 666 case ConversionSpecifier::oArg: 667 case ConversionSpecifier::uArg: 668 case ConversionSpecifier::xArg: 669 case ConversionSpecifier::XArg: 670 case ConversionSpecifier::aArg: 671 case ConversionSpecifier::AArg: 672 case ConversionSpecifier::eArg: 673 case ConversionSpecifier::EArg: 674 case ConversionSpecifier::fArg: 675 case ConversionSpecifier::FArg: 676 case ConversionSpecifier::gArg: 677 case ConversionSpecifier::GArg: 678 case ConversionSpecifier::sArg: 679 return true; 680 681 default: 682 return false; 683 } 684} 685bool PrintfSpecifier::hasValidFieldWidth() const { 686 if (FieldWidth.getHowSpecified() == OptionalAmount::NotSpecified) 687 return true; 688 689 // The field width is valid for all conversions except n 690 switch (CS.getKind()) { 691 case ConversionSpecifier::nArg: 692 return false; 693 694 default: 695 return true; 696 } 697} 698