| // FormatString.cpp - Common stuff for handling printf/scanf formats -*- C++ -*- |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Shared details for processing format strings of printf and scanf |
| // (and friends). |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "FormatStringParsing.h" |
| #include "clang/Basic/LangOptions.h" |
| #include "clang/Basic/TargetInfo.h" |
| #include "llvm/Support/ConvertUTF.h" |
| |
| using clang::analyze_format_string::ArgType; |
| using clang::analyze_format_string::FormatStringHandler; |
| using clang::analyze_format_string::FormatSpecifier; |
| using clang::analyze_format_string::LengthModifier; |
| using clang::analyze_format_string::OptionalAmount; |
| using clang::analyze_format_string::PositionContext; |
| using clang::analyze_format_string::ConversionSpecifier; |
| using namespace clang; |
| |
| // Key function to FormatStringHandler. |
| FormatStringHandler::~FormatStringHandler() {} |
| |
| //===----------------------------------------------------------------------===// |
| // Functions for parsing format strings components in both printf and |
| // scanf format strings. |
| //===----------------------------------------------------------------------===// |
| |
| OptionalAmount |
| clang::analyze_format_string::ParseAmount(const char *&Beg, const char *E) { |
| const char *I = Beg; |
| UpdateOnReturn <const char*> UpdateBeg(Beg, I); |
| |
| unsigned accumulator = 0; |
| bool hasDigits = false; |
| |
| for ( ; I != E; ++I) { |
| char c = *I; |
| if (c >= '0' && c <= '9') { |
| hasDigits = true; |
| accumulator = (accumulator * 10) + (c - '0'); |
| continue; |
| } |
| |
| if (hasDigits) |
| return OptionalAmount(OptionalAmount::Constant, accumulator, Beg, I - Beg, |
| false); |
| |
| break; |
| } |
| |
| return OptionalAmount(); |
| } |
| |
| OptionalAmount |
| clang::analyze_format_string::ParseNonPositionAmount(const char *&Beg, |
| const char *E, |
| unsigned &argIndex) { |
| if (*Beg == '*') { |
| ++Beg; |
| return OptionalAmount(OptionalAmount::Arg, argIndex++, Beg, 0, false); |
| } |
| |
| return ParseAmount(Beg, E); |
| } |
| |
| OptionalAmount |
| clang::analyze_format_string::ParsePositionAmount(FormatStringHandler &H, |
| const char *Start, |
| const char *&Beg, |
| const char *E, |
| PositionContext p) { |
| if (*Beg == '*') { |
| const char *I = Beg + 1; |
| const OptionalAmount &Amt = ParseAmount(I, E); |
| |
| if (Amt.getHowSpecified() == OptionalAmount::NotSpecified) { |
| H.HandleInvalidPosition(Beg, I - Beg, p); |
| return OptionalAmount(false); |
| } |
| |
| if (I == E) { |
| // No more characters left? |
| H.HandleIncompleteSpecifier(Start, E - Start); |
| return OptionalAmount(false); |
| } |
| |
| assert(Amt.getHowSpecified() == OptionalAmount::Constant); |
| |
| if (*I == '$') { |
| // Handle positional arguments |
| |
| // Special case: '*0$', since this is an easy mistake. |
| if (Amt.getConstantAmount() == 0) { |
| H.HandleZeroPosition(Beg, I - Beg + 1); |
| return OptionalAmount(false); |
| } |
| |
| const char *Tmp = Beg; |
| Beg = ++I; |
| |
| return OptionalAmount(OptionalAmount::Arg, Amt.getConstantAmount() - 1, |
| Tmp, 0, true); |
| } |
| |
| H.HandleInvalidPosition(Beg, I - Beg, p); |
| return OptionalAmount(false); |
| } |
| |
| return ParseAmount(Beg, E); |
| } |
| |
| |
| bool |
| clang::analyze_format_string::ParseFieldWidth(FormatStringHandler &H, |
| FormatSpecifier &CS, |
| const char *Start, |
| const char *&Beg, const char *E, |
| unsigned *argIndex) { |
| // FIXME: Support negative field widths. |
| if (argIndex) { |
| CS.setFieldWidth(ParseNonPositionAmount(Beg, E, *argIndex)); |
| } |
| else { |
| const OptionalAmount Amt = |
| ParsePositionAmount(H, Start, Beg, E, |
| analyze_format_string::FieldWidthPos); |
| |
| if (Amt.isInvalid()) |
| return true; |
| CS.setFieldWidth(Amt); |
| } |
| return false; |
| } |
| |
| bool |
| clang::analyze_format_string::ParseArgPosition(FormatStringHandler &H, |
| FormatSpecifier &FS, |
| const char *Start, |
| const char *&Beg, |
| const char *E) { |
| const char *I = Beg; |
| |
| const OptionalAmount &Amt = ParseAmount(I, E); |
| |
| if (I == E) { |
| // No more characters left? |
| H.HandleIncompleteSpecifier(Start, E - Start); |
| return true; |
| } |
| |
| if (Amt.getHowSpecified() == OptionalAmount::Constant && *(I++) == '$') { |
| // Warn that positional arguments are non-standard. |
| H.HandlePosition(Start, I - Start); |
| |
| // Special case: '%0$', since this is an easy mistake. |
| if (Amt.getConstantAmount() == 0) { |
| H.HandleZeroPosition(Start, I - Start); |
| return true; |
| } |
| |
| FS.setArgIndex(Amt.getConstantAmount() - 1); |
| FS.setUsesPositionalArg(); |
| // Update the caller's pointer if we decided to consume |
| // these characters. |
| Beg = I; |
| return false; |
| } |
| |
| return false; |
| } |
| |
| bool |
| clang::analyze_format_string::ParseVectorModifier(FormatStringHandler &H, |
| FormatSpecifier &FS, |
| const char *&I, |
| const char *E, |
| const LangOptions &LO) { |
| if (!LO.OpenCL) |
| return false; |
| |
| const char *Start = I; |
| if (*I == 'v') { |
| ++I; |
| |
| if (I == E) { |
| H.HandleIncompleteSpecifier(Start, E - Start); |
| return true; |
| } |
| |
| OptionalAmount NumElts = ParseAmount(I, E); |
| if (NumElts.getHowSpecified() != OptionalAmount::Constant) { |
| H.HandleIncompleteSpecifier(Start, E - Start); |
| return true; |
| } |
| |
| FS.setVectorNumElts(NumElts); |
| } |
| |
| return false; |
| } |
| |
| bool |
| clang::analyze_format_string::ParseLengthModifier(FormatSpecifier &FS, |
| const char *&I, |
| const char *E, |
| const LangOptions &LO, |
| bool IsScanf) { |
| LengthModifier::Kind lmKind = LengthModifier::None; |
| const char *lmPosition = I; |
| switch (*I) { |
| default: |
| return false; |
| case 'h': |
| ++I; |
| if (I != E && *I == 'h') { |
| ++I; |
| lmKind = LengthModifier::AsChar; |
| } else if (I != E && *I == 'l' && LO.OpenCL) { |
| ++I; |
| lmKind = LengthModifier::AsShortLong; |
| } else { |
| lmKind = LengthModifier::AsShort; |
| } |
| break; |
| case 'l': |
| ++I; |
| if (I != E && *I == 'l') { |
| ++I; |
| lmKind = LengthModifier::AsLongLong; |
| } else { |
| lmKind = LengthModifier::AsLong; |
| } |
| break; |
| case 'j': lmKind = LengthModifier::AsIntMax; ++I; break; |
| case 'z': lmKind = LengthModifier::AsSizeT; ++I; break; |
| case 't': lmKind = LengthModifier::AsPtrDiff; ++I; break; |
| case 'L': lmKind = LengthModifier::AsLongDouble; ++I; break; |
| case 'q': lmKind = LengthModifier::AsQuad; ++I; break; |
| case 'a': |
| if (IsScanf && !LO.C99 && !LO.CPlusPlus11) { |
| // For scanf in C90, look at the next character to see if this should |
| // be parsed as the GNU extension 'a' length modifier. If not, this |
| // will be parsed as a conversion specifier. |
| ++I; |
| if (I != E && (*I == 's' || *I == 'S' || *I == '[')) { |
| lmKind = LengthModifier::AsAllocate; |
| break; |
| } |
| --I; |
| } |
| return false; |
| case 'm': |
| if (IsScanf) { |
| lmKind = LengthModifier::AsMAllocate; |
| ++I; |
| break; |
| } |
| return false; |
| // printf: AsInt64, AsInt32, AsInt3264 |
| // scanf: AsInt64 |
| case 'I': |
| if (I + 1 != E && I + 2 != E) { |
| if (I[1] == '6' && I[2] == '4') { |
| I += 3; |
| lmKind = LengthModifier::AsInt64; |
| break; |
| } |
| if (IsScanf) |
| return false; |
| |
| if (I[1] == '3' && I[2] == '2') { |
| I += 3; |
| lmKind = LengthModifier::AsInt32; |
| break; |
| } |
| } |
| ++I; |
| lmKind = LengthModifier::AsInt3264; |
| break; |
| case 'w': |
| lmKind = LengthModifier::AsWide; ++I; break; |
| } |
| LengthModifier lm(lmPosition, lmKind); |
| FS.setLengthModifier(lm); |
| return true; |
| } |
| |
| bool clang::analyze_format_string::ParseUTF8InvalidSpecifier( |
| const char *SpecifierBegin, const char *FmtStrEnd, unsigned &Len) { |
| if (SpecifierBegin + 1 >= FmtStrEnd) |
| return false; |
| |
| const llvm::UTF8 *SB = |
| reinterpret_cast<const llvm::UTF8 *>(SpecifierBegin + 1); |
| const llvm::UTF8 *SE = reinterpret_cast<const llvm::UTF8 *>(FmtStrEnd); |
| const char FirstByte = *SB; |
| |
| // If the invalid specifier is a multibyte UTF-8 string, return the |
| // total length accordingly so that the conversion specifier can be |
| // properly updated to reflect a complete UTF-8 specifier. |
| unsigned NumBytes = llvm::getNumBytesForUTF8(FirstByte); |
| if (NumBytes == 1) |
| return false; |
| if (SB + NumBytes > SE) |
| return false; |
| |
| Len = NumBytes + 1; |
| return true; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Methods on ArgType. |
| //===----------------------------------------------------------------------===// |
| |
| clang::analyze_format_string::ArgType::MatchKind |
| ArgType::matchesType(ASTContext &C, QualType argTy) const { |
| if (Ptr) { |
| // It has to be a pointer. |
| const PointerType *PT = argTy->getAs<PointerType>(); |
| if (!PT) |
| return NoMatch; |
| |
| // We cannot write through a const qualified pointer. |
| if (PT->getPointeeType().isConstQualified()) |
| return NoMatch; |
| |
| argTy = PT->getPointeeType(); |
| } |
| |
| switch (K) { |
| case InvalidTy: |
| llvm_unreachable("ArgType must be valid"); |
| |
| case UnknownTy: |
| return Match; |
| |
| case AnyCharTy: { |
| if (const EnumType *ETy = argTy->getAs<EnumType>()) { |
| // If the enum is incomplete we know nothing about the underlying type. |
| // Assume that it's 'int'. |
| if (!ETy->getDecl()->isComplete()) |
| return NoMatch; |
| argTy = ETy->getDecl()->getIntegerType(); |
| } |
| |
| if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) |
| switch (BT->getKind()) { |
| default: |
| break; |
| case BuiltinType::Char_S: |
| case BuiltinType::SChar: |
| case BuiltinType::UChar: |
| case BuiltinType::Char_U: |
| case BuiltinType::Bool: |
| return Match; |
| } |
| return NoMatch; |
| } |
| |
| case SpecificTy: { |
| if (const EnumType *ETy = argTy->getAs<EnumType>()) { |
| // If the enum is incomplete we know nothing about the underlying type. |
| // Assume that it's 'int'. |
| if (!ETy->getDecl()->isComplete()) |
| argTy = C.IntTy; |
| else |
| argTy = ETy->getDecl()->getIntegerType(); |
| } |
| argTy = C.getCanonicalType(argTy).getUnqualifiedType(); |
| |
| if (T == argTy) |
| return Match; |
| // Check for "compatible types". |
| if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) |
| switch (BT->getKind()) { |
| default: |
| break; |
| case BuiltinType::Char_S: |
| case BuiltinType::SChar: |
| case BuiltinType::Char_U: |
| case BuiltinType::UChar: |
| case BuiltinType::Bool: |
| if (T == C.UnsignedShortTy || T == C.ShortTy) |
| return NoMatchTypeConfusion; |
| return T == C.UnsignedCharTy || T == C.SignedCharTy ? Match |
| : NoMatch; |
| case BuiltinType::Short: |
| return T == C.UnsignedShortTy ? Match : NoMatch; |
| case BuiltinType::UShort: |
| return T == C.ShortTy ? Match : NoMatch; |
| case BuiltinType::Int: |
| return T == C.UnsignedIntTy ? Match : NoMatch; |
| case BuiltinType::UInt: |
| return T == C.IntTy ? Match : NoMatch; |
| case BuiltinType::Long: |
| return T == C.UnsignedLongTy ? Match : NoMatch; |
| case BuiltinType::ULong: |
| return T == C.LongTy ? Match : NoMatch; |
| case BuiltinType::LongLong: |
| return T == C.UnsignedLongLongTy ? Match : NoMatch; |
| case BuiltinType::ULongLong: |
| return T == C.LongLongTy ? Match : NoMatch; |
| } |
| return NoMatch; |
| } |
| |
| case CStrTy: { |
| const PointerType *PT = argTy->getAs<PointerType>(); |
| if (!PT) |
| return NoMatch; |
| QualType pointeeTy = PT->getPointeeType(); |
| if (const BuiltinType *BT = pointeeTy->getAs<BuiltinType>()) |
| switch (BT->getKind()) { |
| case BuiltinType::Void: |
| case BuiltinType::Char_U: |
| case BuiltinType::UChar: |
| case BuiltinType::Char_S: |
| case BuiltinType::SChar: |
| return Match; |
| default: |
| break; |
| } |
| |
| return NoMatch; |
| } |
| |
| case WCStrTy: { |
| const PointerType *PT = argTy->getAs<PointerType>(); |
| if (!PT) |
| return NoMatch; |
| QualType pointeeTy = |
| C.getCanonicalType(PT->getPointeeType()).getUnqualifiedType(); |
| return pointeeTy == C.getWideCharType() ? Match : NoMatch; |
| } |
| |
| case WIntTy: { |
| QualType WInt = C.getCanonicalType(C.getWIntType()).getUnqualifiedType(); |
| |
| if (C.getCanonicalType(argTy).getUnqualifiedType() == WInt) |
| return Match; |
| |
| QualType PromoArg = argTy->isPromotableIntegerType() |
| ? C.getPromotedIntegerType(argTy) |
| : argTy; |
| PromoArg = C.getCanonicalType(PromoArg).getUnqualifiedType(); |
| |
| // If the promoted argument is the corresponding signed type of the |
| // wint_t type, then it should match. |
| if (PromoArg->hasSignedIntegerRepresentation() && |
| C.getCorrespondingUnsignedType(PromoArg) == WInt) |
| return Match; |
| |
| return WInt == PromoArg ? Match : NoMatch; |
| } |
| |
| case CPointerTy: |
| if (argTy->isVoidPointerType()) { |
| return Match; |
| } if (argTy->isPointerType() || argTy->isObjCObjectPointerType() || |
| argTy->isBlockPointerType() || argTy->isNullPtrType()) { |
| return NoMatchPedantic; |
| } else { |
| return NoMatch; |
| } |
| |
| case ObjCPointerTy: { |
| if (argTy->getAs<ObjCObjectPointerType>() || |
| argTy->getAs<BlockPointerType>()) |
| return Match; |
| |
| // Handle implicit toll-free bridging. |
| if (const PointerType *PT = argTy->getAs<PointerType>()) { |
| // Things such as CFTypeRef are really just opaque pointers |
| // to C structs representing CF types that can often be bridged |
| // to Objective-C objects. Since the compiler doesn't know which |
| // structs can be toll-free bridged, we just accept them all. |
| QualType pointee = PT->getPointeeType(); |
| if (pointee->getAsStructureType() || pointee->isVoidType()) |
| return Match; |
| } |
| return NoMatch; |
| } |
| } |
| |
| llvm_unreachable("Invalid ArgType Kind!"); |
| } |
| |
| ArgType ArgType::makeVectorType(ASTContext &C, unsigned NumElts) const { |
| // Check for valid vector element types. |
| if (T.isNull()) |
| return ArgType::Invalid(); |
| |
| QualType Vec = C.getExtVectorType(T, NumElts); |
| return ArgType(Vec, Name); |
| } |
| |
| QualType ArgType::getRepresentativeType(ASTContext &C) const { |
| QualType Res; |
| switch (K) { |
| case InvalidTy: |
| llvm_unreachable("No representative type for Invalid ArgType"); |
| case UnknownTy: |
| llvm_unreachable("No representative type for Unknown ArgType"); |
| case AnyCharTy: |
| Res = C.CharTy; |
| break; |
| case SpecificTy: |
| Res = T; |
| break; |
| case CStrTy: |
| Res = C.getPointerType(C.CharTy); |
| break; |
| case WCStrTy: |
| Res = C.getPointerType(C.getWideCharType()); |
| break; |
| case ObjCPointerTy: |
| Res = C.ObjCBuiltinIdTy; |
| break; |
| case CPointerTy: |
| Res = C.VoidPtrTy; |
| break; |
| case WIntTy: { |
| Res = C.getWIntType(); |
| break; |
| } |
| } |
| |
| if (Ptr) |
| Res = C.getPointerType(Res); |
| return Res; |
| } |
| |
| std::string ArgType::getRepresentativeTypeName(ASTContext &C) const { |
| std::string S = getRepresentativeType(C).getAsString(); |
| |
| std::string Alias; |
| if (Name) { |
| // Use a specific name for this type, e.g. "size_t". |
| Alias = Name; |
| if (Ptr) { |
| // If ArgType is actually a pointer to T, append an asterisk. |
| Alias += (Alias[Alias.size()-1] == '*') ? "*" : " *"; |
| } |
| // If Alias is the same as the underlying type, e.g. wchar_t, then drop it. |
| if (S == Alias) |
| Alias.clear(); |
| } |
| |
| if (!Alias.empty()) |
| return std::string("'") + Alias + "' (aka '" + S + "')"; |
| return std::string("'") + S + "'"; |
| } |
| |
| |
| //===----------------------------------------------------------------------===// |
| // Methods on OptionalAmount. |
| //===----------------------------------------------------------------------===// |
| |
| ArgType |
| analyze_format_string::OptionalAmount::getArgType(ASTContext &Ctx) const { |
| return Ctx.IntTy; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Methods on LengthModifier. |
| //===----------------------------------------------------------------------===// |
| |
| const char * |
| analyze_format_string::LengthModifier::toString() const { |
| switch (kind) { |
| case AsChar: |
| return "hh"; |
| case AsShort: |
| return "h"; |
| case AsShortLong: |
| return "hl"; |
| case AsLong: // or AsWideChar |
| return "l"; |
| case AsLongLong: |
| return "ll"; |
| case AsQuad: |
| return "q"; |
| case AsIntMax: |
| return "j"; |
| case AsSizeT: |
| return "z"; |
| case AsPtrDiff: |
| return "t"; |
| case AsInt32: |
| return "I32"; |
| case AsInt3264: |
| return "I"; |
| case AsInt64: |
| return "I64"; |
| case AsLongDouble: |
| return "L"; |
| case AsAllocate: |
| return "a"; |
| case AsMAllocate: |
| return "m"; |
| case AsWide: |
| return "w"; |
| case None: |
| return ""; |
| } |
| return nullptr; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Methods on ConversionSpecifier. |
| //===----------------------------------------------------------------------===// |
| |
| const char *ConversionSpecifier::toString() const { |
| switch (kind) { |
| case dArg: return "d"; |
| case DArg: return "D"; |
| case iArg: return "i"; |
| case oArg: return "o"; |
| case OArg: return "O"; |
| case uArg: return "u"; |
| case UArg: return "U"; |
| case xArg: return "x"; |
| case XArg: return "X"; |
| case fArg: return "f"; |
| case FArg: return "F"; |
| case eArg: return "e"; |
| case EArg: return "E"; |
| case gArg: return "g"; |
| case GArg: return "G"; |
| case aArg: return "a"; |
| case AArg: return "A"; |
| case cArg: return "c"; |
| case sArg: return "s"; |
| case pArg: return "p"; |
| case PArg: |
| return "P"; |
| case nArg: return "n"; |
| case PercentArg: return "%"; |
| case ScanListArg: return "["; |
| case InvalidSpecifier: return nullptr; |
| |
| // POSIX unicode extensions. |
| case CArg: return "C"; |
| case SArg: return "S"; |
| |
| // Objective-C specific specifiers. |
| case ObjCObjArg: return "@"; |
| |
| // FreeBSD kernel specific specifiers. |
| case FreeBSDbArg: return "b"; |
| case FreeBSDDArg: return "D"; |
| case FreeBSDrArg: return "r"; |
| case FreeBSDyArg: return "y"; |
| |
| // GlibC specific specifiers. |
| case PrintErrno: return "m"; |
| |
| // MS specific specifiers. |
| case ZArg: return "Z"; |
| } |
| return nullptr; |
| } |
| |
| Optional<ConversionSpecifier> |
| ConversionSpecifier::getStandardSpecifier() const { |
| ConversionSpecifier::Kind NewKind; |
| |
| switch (getKind()) { |
| default: |
| return None; |
| case DArg: |
| NewKind = dArg; |
| break; |
| case UArg: |
| NewKind = uArg; |
| break; |
| case OArg: |
| NewKind = oArg; |
| break; |
| } |
| |
| ConversionSpecifier FixedCS(*this); |
| FixedCS.setKind(NewKind); |
| return FixedCS; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Methods on OptionalAmount. |
| //===----------------------------------------------------------------------===// |
| |
| void OptionalAmount::toString(raw_ostream &os) const { |
| switch (hs) { |
| case Invalid: |
| case NotSpecified: |
| return; |
| case Arg: |
| if (UsesDotPrefix) |
| os << "."; |
| if (usesPositionalArg()) |
| os << "*" << getPositionalArgIndex() << "$"; |
| else |
| os << "*"; |
| break; |
| case Constant: |
| if (UsesDotPrefix) |
| os << "."; |
| os << amt; |
| break; |
| } |
| } |
| |
| bool FormatSpecifier::hasValidLengthModifier(const TargetInfo &Target, |
| const LangOptions &LO) const { |
| switch (LM.getKind()) { |
| case LengthModifier::None: |
| return true; |
| |
| // Handle most integer flags |
| case LengthModifier::AsShort: |
| // Length modifier only applies to FP vectors. |
| if (LO.OpenCL && CS.isDoubleArg()) |
| return !VectorNumElts.isInvalid(); |
| |
| if (Target.getTriple().isOSMSVCRT()) { |
| switch (CS.getKind()) { |
| case ConversionSpecifier::cArg: |
| case ConversionSpecifier::CArg: |
| case ConversionSpecifier::sArg: |
| case ConversionSpecifier::SArg: |
| case ConversionSpecifier::ZArg: |
| return true; |
| default: |
| break; |
| } |
| } |
| LLVM_FALLTHROUGH; |
| case LengthModifier::AsChar: |
| case LengthModifier::AsLongLong: |
| case LengthModifier::AsQuad: |
| case LengthModifier::AsIntMax: |
| case LengthModifier::AsSizeT: |
| case LengthModifier::AsPtrDiff: |
| switch (CS.getKind()) { |
| case ConversionSpecifier::dArg: |
| case ConversionSpecifier::DArg: |
| case ConversionSpecifier::iArg: |
| case ConversionSpecifier::oArg: |
| case ConversionSpecifier::OArg: |
| case ConversionSpecifier::uArg: |
| case ConversionSpecifier::UArg: |
| case ConversionSpecifier::xArg: |
| case ConversionSpecifier::XArg: |
| case ConversionSpecifier::nArg: |
| return true; |
| case ConversionSpecifier::FreeBSDrArg: |
| case ConversionSpecifier::FreeBSDyArg: |
| return Target.getTriple().isOSFreeBSD() || Target.getTriple().isPS4(); |
| default: |
| return false; |
| } |
| |
| case LengthModifier::AsShortLong: |
| return LO.OpenCL && !VectorNumElts.isInvalid(); |
| |
| // Handle 'l' flag |
| case LengthModifier::AsLong: // or AsWideChar |
| if (CS.isDoubleArg()) { |
| // Invalid for OpenCL FP scalars. |
| if (LO.OpenCL && VectorNumElts.isInvalid()) |
| return false; |
| return true; |
| } |
| |
| switch (CS.getKind()) { |
| case ConversionSpecifier::dArg: |
| case ConversionSpecifier::DArg: |
| case ConversionSpecifier::iArg: |
| case ConversionSpecifier::oArg: |
| case ConversionSpecifier::OArg: |
| case ConversionSpecifier::uArg: |
| case ConversionSpecifier::UArg: |
| case ConversionSpecifier::xArg: |
| case ConversionSpecifier::XArg: |
| case ConversionSpecifier::nArg: |
| case ConversionSpecifier::cArg: |
| case ConversionSpecifier::sArg: |
| case ConversionSpecifier::ScanListArg: |
| case ConversionSpecifier::ZArg: |
| return true; |
| case ConversionSpecifier::FreeBSDrArg: |
| case ConversionSpecifier::FreeBSDyArg: |
| return Target.getTriple().isOSFreeBSD() || Target.getTriple().isPS4(); |
| 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; |
| // GNU libc extension. |
| case ConversionSpecifier::dArg: |
| case ConversionSpecifier::iArg: |
| case ConversionSpecifier::oArg: |
| case ConversionSpecifier::uArg: |
| case ConversionSpecifier::xArg: |
| case ConversionSpecifier::XArg: |
| return !Target.getTriple().isOSDarwin() && |
| !Target.getTriple().isOSWindows(); |
| default: |
| return false; |
| } |
| |
| case LengthModifier::AsAllocate: |
| switch (CS.getKind()) { |
| case ConversionSpecifier::sArg: |
| case ConversionSpecifier::SArg: |
| case ConversionSpecifier::ScanListArg: |
| return true; |
| default: |
| return false; |
| } |
| |
| case LengthModifier::AsMAllocate: |
| switch (CS.getKind()) { |
| case ConversionSpecifier::cArg: |
| case ConversionSpecifier::CArg: |
| case ConversionSpecifier::sArg: |
| case ConversionSpecifier::SArg: |
| case ConversionSpecifier::ScanListArg: |
| return true; |
| default: |
| return false; |
| } |
| case LengthModifier::AsInt32: |
| case LengthModifier::AsInt3264: |
| case LengthModifier::AsInt64: |
| switch (CS.getKind()) { |
| case ConversionSpecifier::dArg: |
| case ConversionSpecifier::iArg: |
| case ConversionSpecifier::oArg: |
| case ConversionSpecifier::uArg: |
| case ConversionSpecifier::xArg: |
| case ConversionSpecifier::XArg: |
| return Target.getTriple().isOSMSVCRT(); |
| default: |
| return false; |
| } |
| case LengthModifier::AsWide: |
| switch (CS.getKind()) { |
| case ConversionSpecifier::cArg: |
| case ConversionSpecifier::CArg: |
| case ConversionSpecifier::sArg: |
| case ConversionSpecifier::SArg: |
| case ConversionSpecifier::ZArg: |
| return Target.getTriple().isOSMSVCRT(); |
| default: |
| return false; |
| } |
| } |
| llvm_unreachable("Invalid LengthModifier Kind!"); |
| } |
| |
| bool FormatSpecifier::hasStandardLengthModifier() const { |
| switch (LM.getKind()) { |
| case LengthModifier::None: |
| case LengthModifier::AsChar: |
| case LengthModifier::AsShort: |
| case LengthModifier::AsLong: |
| case LengthModifier::AsLongLong: |
| case LengthModifier::AsIntMax: |
| case LengthModifier::AsSizeT: |
| case LengthModifier::AsPtrDiff: |
| case LengthModifier::AsLongDouble: |
| return true; |
| case LengthModifier::AsAllocate: |
| case LengthModifier::AsMAllocate: |
| case LengthModifier::AsQuad: |
| case LengthModifier::AsInt32: |
| case LengthModifier::AsInt3264: |
| case LengthModifier::AsInt64: |
| case LengthModifier::AsWide: |
| case LengthModifier::AsShortLong: // ??? |
| return false; |
| } |
| llvm_unreachable("Invalid LengthModifier Kind!"); |
| } |
| |
| bool FormatSpecifier::hasStandardConversionSpecifier( |
| const LangOptions &LangOpt) const { |
| switch (CS.getKind()) { |
| case ConversionSpecifier::cArg: |
| case ConversionSpecifier::dArg: |
| case ConversionSpecifier::iArg: |
| case ConversionSpecifier::oArg: |
| case ConversionSpecifier::uArg: |
| case ConversionSpecifier::xArg: |
| case ConversionSpecifier::XArg: |
| case ConversionSpecifier::fArg: |
| case ConversionSpecifier::FArg: |
| case ConversionSpecifier::eArg: |
| case ConversionSpecifier::EArg: |
| case ConversionSpecifier::gArg: |
| case ConversionSpecifier::GArg: |
| case ConversionSpecifier::aArg: |
| case ConversionSpecifier::AArg: |
| case ConversionSpecifier::sArg: |
| case ConversionSpecifier::pArg: |
| case ConversionSpecifier::nArg: |
| case ConversionSpecifier::ObjCObjArg: |
| case ConversionSpecifier::ScanListArg: |
| case ConversionSpecifier::PercentArg: |
| case ConversionSpecifier::PArg: |
| return true; |
| case ConversionSpecifier::CArg: |
| case ConversionSpecifier::SArg: |
| return LangOpt.ObjC; |
| case ConversionSpecifier::InvalidSpecifier: |
| case ConversionSpecifier::FreeBSDbArg: |
| case ConversionSpecifier::FreeBSDDArg: |
| case ConversionSpecifier::FreeBSDrArg: |
| case ConversionSpecifier::FreeBSDyArg: |
| case ConversionSpecifier::PrintErrno: |
| case ConversionSpecifier::DArg: |
| case ConversionSpecifier::OArg: |
| case ConversionSpecifier::UArg: |
| case ConversionSpecifier::ZArg: |
| return false; |
| } |
| llvm_unreachable("Invalid ConversionSpecifier Kind!"); |
| } |
| |
| bool FormatSpecifier::hasStandardLengthConversionCombination() const { |
| if (LM.getKind() == LengthModifier::AsLongDouble) { |
| switch(CS.getKind()) { |
| case ConversionSpecifier::dArg: |
| case ConversionSpecifier::iArg: |
| case ConversionSpecifier::oArg: |
| case ConversionSpecifier::uArg: |
| case ConversionSpecifier::xArg: |
| case ConversionSpecifier::XArg: |
| return false; |
| default: |
| return true; |
| } |
| } |
| return true; |
| } |
| |
| Optional<LengthModifier> FormatSpecifier::getCorrectedLengthModifier() const { |
| if (CS.isAnyIntArg() || CS.getKind() == ConversionSpecifier::nArg) { |
| if (LM.getKind() == LengthModifier::AsLongDouble || |
| LM.getKind() == LengthModifier::AsQuad) { |
| LengthModifier FixedLM(LM); |
| FixedLM.setKind(LengthModifier::AsLongLong); |
| return FixedLM; |
| } |
| } |
| |
| return None; |
| } |
| |
| bool FormatSpecifier::namedTypeToLengthModifier(QualType QT, |
| LengthModifier &LM) { |
| assert(isa<TypedefType>(QT) && "Expected a TypedefType"); |
| const TypedefNameDecl *Typedef = cast<TypedefType>(QT)->getDecl(); |
| |
| for (;;) { |
| const IdentifierInfo *Identifier = Typedef->getIdentifier(); |
| if (Identifier->getName() == "size_t") { |
| LM.setKind(LengthModifier::AsSizeT); |
| return true; |
| } else if (Identifier->getName() == "ssize_t") { |
| // Not C99, but common in Unix. |
| LM.setKind(LengthModifier::AsSizeT); |
| return true; |
| } else if (Identifier->getName() == "intmax_t") { |
| LM.setKind(LengthModifier::AsIntMax); |
| return true; |
| } else if (Identifier->getName() == "uintmax_t") { |
| LM.setKind(LengthModifier::AsIntMax); |
| return true; |
| } else if (Identifier->getName() == "ptrdiff_t") { |
| LM.setKind(LengthModifier::AsPtrDiff); |
| return true; |
| } |
| |
| QualType T = Typedef->getUnderlyingType(); |
| if (!isa<TypedefType>(T)) |
| break; |
| |
| Typedef = cast<TypedefType>(T)->getDecl(); |
| } |
| return false; |
| } |