Félix Cloutier cf239c2f17 [NFC] Make format() more amenable to format attributes
This change modifies the implementation of the format() function
so that vendor forks committed to building with compilers that
support __attribute__((format)) on non-variadic functions can
check the format() function with it.

rdar://84571523
2022-11-02 13:56:51 -07:00

371 lines
9.5 KiB
C++

//===- Format.cpp - Efficient printf-style formatting for streams -*- 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
//
//===----------------------------------------------------------------------===//
//
// This file implements the non-template part of Format.h, which is used to
// provide a type-safe-ish interface to printf-style formatting.
//
//===----------------------------------------------------------------------===//
#include "llvm/Support/Format.h"
namespace {
/// Enum representation of a printf-style length specifier.
enum ArgLength : char {
/// Corresponds to 'hh' length specifier.
AL_ShortShort,
/// Corresponds to 'h' length specifier.
AL_Short,
/// Corresponds to default length specifier.
AL_Default,
/// Corresponds to 'l' length specifier.
AL_Long,
/// Corresponds to 'll' length specifier.
AL_LongLong,
/// Corresponds to 'j' length specifier.
AL_IntMax,
/// Corresponds to 'z' length specifier.
AL_Size,
/// Corresponds to 't' length specifier.
AL_Ptrdiff,
/// Corresponds to 'L' length specifier.
AL_LongDouble,
/// First invalid value of \p ArgLength.
AL_End,
};
/// Enum representation of a printf-style specifier.
enum SpecifierChar : char {
/// Corresponds to any of 'd', 'i', 'u', 'o', 'x' or 'X' specifiers.
SC_Int,
/// Corresponds to any of 'f', 'F', 'e', 'E', 'g', 'G', 'a' or 'A' specifiers.
SC_Float,
/// Corresponds to 'c' specifier.
SC_Char,
/// Corresponds to 's' specifier.
SC_String,
/// Corresponds to 'p' specifier.
SC_VoidPointer,
/// Corresponds to 'n' specifier.
SC_Count,
/// First invalid value of \p SpecifierChar.
SC_End,
};
constexpr uint64_t specifierBit(char C) {
// specifierMask builds a bit map where each set bit indicates that the
// character whose ASCII value is 64 + <bit index> would be legal to use
// as a format specifier in the current parsing context.
// To cover all ASCII characters, we would need 128 bits; however, the only
// character with an ASCII value less than 64 that can be used as a specifier
// is % (as in %%), so we save some space and complexity by dropping the
// lower half of the bit map, which is going to be all zeroes anyway.
// % is handled as a special case.
return (uint64_t)1 << (C - 64);
}
template <size_t N>
constexpr /* consteval */ uint64_t specifierMask(const char (&Specifiers)[N]) {
uint64_t Mask = 0;
for (const char *I = std::begin(Specifiers); I != std::end(Specifiers); ++I) {
if (*I == 0)
break;
Mask |= specifierBit(*I);
}
return Mask;
}
constexpr auto ST_Unknown = llvm::PrintfStyleFormatReader::ST_Unknown;
constexpr auto ST_WideChar = llvm::PrintfStyleFormatReader::ST_WideChar;
constexpr auto ST_Int = llvm::PrintfStyleFormatReader::ST_Int;
constexpr auto ST_Long = llvm::PrintfStyleFormatReader::ST_Long;
constexpr auto ST_LongLong = llvm::PrintfStyleFormatReader::ST_LongLong;
constexpr auto ST_IntMax = llvm::PrintfStyleFormatReader::ST_IntMax;
constexpr auto ST_Size = llvm::PrintfStyleFormatReader::ST_Size;
constexpr auto ST_Ptrdiff = llvm::PrintfStyleFormatReader::ST_Ptrdiff;
constexpr auto ST_Double = llvm::PrintfStyleFormatReader::ST_Double;
constexpr auto ST_LongDouble = llvm::PrintfStyleFormatReader::ST_LongDouble;
constexpr auto ST_CString = llvm::PrintfStyleFormatReader::ST_CString;
constexpr auto ST_WideCString = llvm::PrintfStyleFormatReader::ST_WideCString;
constexpr auto ST_VoidPointer = llvm::PrintfStyleFormatReader::ST_VoidPointer;
constexpr auto ST_Count_Char = llvm::PrintfStyleFormatReader::ST_Count_Char;
constexpr auto ST_Count_Short = llvm::PrintfStyleFormatReader::ST_Count_Short;
constexpr auto ST_Count_Int = llvm::PrintfStyleFormatReader::ST_Count_Int;
constexpr auto ST_Count_Long = llvm::PrintfStyleFormatReader::ST_Count_Long;
constexpr auto ST_Count_LongLong =
llvm::PrintfStyleFormatReader::ST_Count_LongLong;
constexpr auto ST_Count_IntMax = llvm::PrintfStyleFormatReader::ST_Count_IntMax;
constexpr auto ST_Count_Size = llvm::PrintfStyleFormatReader::ST_Count_Size;
constexpr auto ST_Count_Ptrdiff =
llvm::PrintfStyleFormatReader::ST_Count_Ptrdiff;
llvm::PrintfStyleFormatReader::SpecifierType SpecifierTable[SC_End][AL_End] = {
{
// SC_Int
ST_Int,
ST_Int,
ST_Int,
ST_Long,
ST_LongLong,
ST_IntMax,
ST_Size,
ST_Ptrdiff,
ST_Unknown,
},
{
// SC_Float
ST_Unknown,
ST_Unknown,
ST_Double,
ST_Double,
ST_Unknown,
ST_Unknown,
ST_Unknown,
ST_Unknown,
ST_LongDouble,
},
{
// SC_Char
ST_Unknown,
ST_Unknown,
ST_Int,
ST_WideChar,
ST_Unknown,
ST_Unknown,
ST_Unknown,
ST_Unknown,
ST_Unknown,
},
{
// SC_String
ST_Unknown,
ST_Unknown,
ST_CString,
ST_WideCString,
ST_Unknown,
ST_Unknown,
ST_Unknown,
ST_Unknown,
ST_Unknown,
},
{
// SC_VoidPointer
ST_Unknown,
ST_Unknown,
ST_VoidPointer,
ST_Unknown,
ST_Unknown,
ST_Unknown,
ST_Unknown,
ST_Unknown,
ST_Unknown,
},
{
// SC_Count
ST_Count_Char,
ST_Count_Short,
ST_Count_Int,
ST_Count_Long,
ST_Count_LongLong,
ST_Count_IntMax,
ST_Count_Size,
ST_Count_Ptrdiff,
ST_Unknown,
},
};
} // namespace
namespace llvm {
void PrintfStyleFormatReader::refillSpecifierQueue() {
if (auto PercentPtr = strchr(Fmt, '%')) {
Fmt = PercentPtr;
} else {
SpecifierQueue.push_back(ST_EndOfFormatString);
return;
}
if (*++Fmt == '%') {
// %% case: skip and try again
++Fmt;
refillSpecifierQueue();
return;
}
// Push ST_Unknown to SpecifierQueue. If we bail out early, this is what
// the caller gets. Fill in real specifiers to Specifiers: if we
// successfully get to the end, then swap Specifiers with SpecifierQueue.
SpecifierQueue.push_back(ST_Unknown);
llvm::SmallVector<SpecifierType, 3> Specifiers;
// Bitfield keeping track of which specifier characters are allowed given
// flags and precision settings. Each bit tells whether ascii character
// 0x40 + <bit index> is allowed as a specifier. '%', which has an ASCII value
// less than 0x40 and does not allow any customization, is handled by a check
// above. The starting value contains all standard specifiers.
uint64_t ValidSpecifiers = specifierMask("diuoxXfFeEgGaAcspn");
// update specifier mask based on flags
bool ReadAllFlags = false;
while (!ReadAllFlags) {
switch (*Fmt) {
case '+':
case '-':
case ' ':
// valid for all specifiers
++Fmt;
break;
case '#':
ValidSpecifiers &= specifierMask("xXaAeEfFgG");
++Fmt;
break;
case '0':
ValidSpecifiers &= specifierMask("diouxXaAeEfFgG");
++Fmt;
break;
default:
ReadAllFlags = true;
break;
}
}
// skip width
if (*Fmt == '*') {
Specifiers.push_back(ST_Int);
++Fmt;
} else
while (*Fmt >= '0' && *Fmt <= '9')
++Fmt;
// test precision
if (*Fmt == '.') {
ValidSpecifiers &= specifierMask("diouxXaAeEfFgGs");
++Fmt;
if (*Fmt == '*') {
Specifiers.push_back(ST_Int);
++Fmt;
} else
while (*Fmt >= '0' && *Fmt <= '9')
++Fmt;
}
// parse length
bool FoundLength = false;
ArgLength AL = AL_Default;
while (!FoundLength) {
ArgLength NewAL;
switch (*Fmt) {
case 'h':
NewAL = AL_Short;
break;
case 'l':
NewAL = AL_Long;
break;
case 'j':
NewAL = AL_IntMax;
break;
case 'z':
NewAL = AL_Size;
break;
case 't':
NewAL = AL_Ptrdiff;
break;
case 'L':
NewAL = AL_LongDouble;
break;
default:
FoundLength = true;
continue;
}
if (NewAL == AL_Long && AL == AL_Long)
AL = AL_LongLong;
else if (NewAL == AL_Short && AL == AL_Short)
AL = AL_ShortShort;
else if (AL == AL_Default)
AL = NewAL;
else
return;
++Fmt;
}
// parse specifier; verify that the character is a valid specifier given
// restrictions imposed by by the use of flags and precision values
char Next = *Fmt;
if (Next == 0)
return;
++Fmt;
if (Next < 0x40 || (specifierBit(Next) & ValidSpecifiers) == 0)
return;
SpecifierChar SC;
switch (Next) {
case 'd':
case 'i':
case 'u':
case 'o':
case 'x':
case 'X':
SC = SC_Int;
break;
case 'a':
case 'A':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
SC = SC_Float;
break;
case 'c':
SC = SC_Char;
break;
case 's':
SC = SC_String;
break;
case 'p':
SC = SC_VoidPointer;
break;
case 'n':
SC = SC_Count;
break;
default:
return;
}
auto Spec = SpecifierTable[SC][AL];
if (Spec == ST_Unknown)
return;
Specifiers.push_back(Spec);
std::reverse(Specifiers.begin(), Specifiers.end());
std::swap(Specifiers, SpecifierQueue);
}
const char *PrintfStyleFormatReader::ensureCompatible(const char *Expected,
const char *Fmt) {
PrintfStyleFormatReader EFR(Expected);
PrintfStyleFormatReader FFR(Fmt);
SpecifierType EST;
do {
EST = EFR.nextSpecifier();
if (EST != FFR.nextSpecifier())
return Expected;
} while (EST);
return Fmt;
}
} // namespace llvm