Index: include/llvm/ADT/STLExtras.h =================================================================== --- include/llvm/ADT/STLExtras.h +++ include/llvm/ADT/STLExtras.h @@ -33,6 +33,11 @@ #include "llvm/Support/Compiler.h" namespace llvm { + +// Only used by compiler if both template types are the same. Useful when +// using SFINAE to test for the existence of member functions. +template struct SameType; + namespace detail { template @@ -388,6 +393,18 @@ template struct rank : rank {}; template <> struct rank<0> {}; +/// \brief traits class for checking whether type T is one of any of the given +/// types in the variadic list. +template struct is_one_of { + static const bool value = false; +}; + +template +struct is_one_of { + static const bool value = + std::is_same::value || is_one_of::value; +}; + //===----------------------------------------------------------------------===// // Extra additions for arrays //===----------------------------------------------------------------------===// Index: include/llvm/Support/FormatProviders.h =================================================================== --- /dev/null +++ include/llvm/Support/FormatProviders.h @@ -0,0 +1,306 @@ +//===- FormatProviders.h - Formatters for common LLVM types -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements format providers for many common LLVM types, for example +// allowing precision and width specifiers for scalar and string types. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FORMATPROVIDERS_H +#define LLVM_SUPPORT_FORMATPROVIDERS_H + +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/NativeFormatting.h" + +#include + +namespace llvm { +namespace detail { +template +struct use_integral_formatter + : public std::integral_constant< + bool, is_one_of::value> {}; + +template +struct use_char_formatter + : public std::integral_constant::value> {}; + +template +struct is_cstring + : public std::integral_constant::value> { +}; + +template +struct use_string_formatter + : public std::integral_constant< + bool, is_one_of::value || + is_cstring::value> {}; + +template +struct use_pointer_formatter + : public std::integral_constant::value && + !is_cstring::value> {}; + +template +struct use_double_formatter + : public std::integral_constant::value> {}; + +LLVM_ATTRIBUTE_ALWAYS_INLINE +Optional parseNumericPrecision(StringRef Str) { + size_t Prec; + Optional Result; + if (Str.empty()) + Result = None; + else if (Str.getAsInteger(10, Prec)) { + assert(false && "Invalid precision specifier"); + Result = None; + } else { + assert(Prec < 100 && "Precision out of range"); + Result = std::min(99u, Prec); + } + return Result; +} +} + +template struct format_provider {}; + +/// \brief implementation of format_provider for integral arithmetic types. +/// +/// The options string of an integral type has the format: +/// +/// [S][##] +/// +/// where S is an optional character sequence specifying the presentation style +/// of the number, and ## is an optional precision specifier of up to 2 decimal +/// digits which represents the precision. The valid options for S are: +/// +/// ========================================================================== +/// | S | Meaning | Example | Precision Meaning | +/// -------------------------------------------------------------------------- +/// | | | Input | Output | | +/// ========================================================================== +/// | x- | Hex no prefix, lower | 42 | 2a | Minimum # digits | +/// | X- | Hex no prefix, upper | 42 | 2A | Minimum # digits | +/// | x+ / x | Hex + prefix, lower | 42 | 0x2a | Minimum # digits | +/// | X+ / X | Hex + prefix, upper | 42 | 0x2A | Minimum # digits | +/// | N / n | Digit grouped number | 123456 | 123,456 | Minimum # digits | +/// | P / p | Percentage | 1 | 100.00% | Desired # decimals | +/// | F / f | Fixed point | 1 | 1.00 | Desired # decimals | +/// | E | Exponential with E | 100000 | 1.0E+05 | Desired # decimals | +/// | e | Exponential with e | 100000 | 1.0e+05 | Desired # decimals | +/// | D / d | Integer | 100000 | 100000 | Minimum # digits | +/// | (empty) | Same as D / d | | | | +/// ========================================================================== +/// +/// The default precision is 6 for exponential (E / e), 2 for Fixed (F / f), and +/// 0 for everything else. + +template +struct format_provider::value>::type> { +private: + struct Options { + IntegerStyle Style; + Optional Precision; + }; + + static Options parseStyle(StringRef S) { + Options Result; + + if (S.consume_front("x-")) + Result.Style = IntegerStyle::HexLowerNoPrefix; + else if (S.consume_front("X-")) + Result.Style = IntegerStyle::HexUpperNoPrefix; + else if (S.consume_front("x+") || S.consume_front("x")) + Result.Style = IntegerStyle::HexLowerPrefix; + else if (S.consume_front("X+") || S.consume_front("X")) + Result.Style = IntegerStyle::HexUpperPrefix; + else if (S.consume_front("N") || S.consume_front("n")) + Result.Style = IntegerStyle::Number; + else if (S.consume_front("P") || S.consume_front("p")) + Result.Style = IntegerStyle::Percent; + else if (S.consume_front("F") || S.consume_front("f")) + Result.Style = IntegerStyle::Fixed; + else if (S.consume_front("E")) + Result.Style = IntegerStyle::ExponentUpper; + else if (S.consume_front("e")) + Result.Style = IntegerStyle::Exponent; + else if (S.consume_front("D") || S.consume_front("d")) + Result.Style = IntegerStyle::Integer; + else + Result.Style = IntegerStyle::Integer; + + Result.Precision = detail::parseNumericPrecision(S); + // TODO: This is a bit of a hack. The formatting library treats the prefix + // as part of the length when computing precision. We don't want this, so + // we have to increment our precision by 2. + if (isPrefixedHexStyle(Result.Style)) + Result.Precision = + 2 + Result.Precision.getValueOr(getDefaultPrecision(Result.Style)); + + return Result; + } + +public: + static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) { + Options S = parseStyle(Style); + write_integer(Stream, V, S.Style, S.Precision); + } +}; + +/// \brief implementation of format_provider for integral pointer types. +/// +/// The options string of an pointer type has the format: +/// +/// [S][##] +/// +/// where S is an optional character sequence specifying the presentation style +/// of the pointer, and ## is an optional precision specifier of up to 2 decimal +/// digits which represents the precision. The valid options for S are: +/// +/// ========================================================================== +/// | S | Meaning | Example | +/// -------------------------------------------------------------------------- +/// | | | Input | Output | +/// ========================================================================== +/// | x- | Hex no prefix, lower | 0xDEADBEEF | deadbeef | +/// | X- | Hex no prefix, upper | 0xDEADBEEF | DEADBEEF | +/// | x+ / x | Hex + prefix, lower | 0xDEADBEEF | 0xdeadbeef | +/// | X+ / X | Hex + prefix, upper | 0xDEADBEEF | 0xDEADBEEF | +/// | (empty) | Same as X+ / X | | | +/// ========================================================================== +/// +/// The default precision is the number of nibbles in a machine word, and in all +/// cases indicates the minimum number of nibbles to print. +template +struct format_provider< + T, typename std::enable_if::value>::type> { +private: + struct Options { + HexStyle Style; + Optional Precision; + }; + + static Options parseStyle(StringRef S) { + Options Result; + if (S.consume_front("x-")) + Result.Style = HexStyle::Lower; + else if (S.consume_front("X-")) + Result.Style = HexStyle::Upper; + else if (S.consume_front("x+") || S.consume_front("x")) + Result.Style = HexStyle::PrefixLower; + else if (S.consume_front("X+") || S.consume_front("X")) + Result.Style = HexStyle::PrefixUpper; + else + Result.Style = HexStyle::PrefixUpper; + + Result.Precision = detail::parseNumericPrecision(S); + if (!Result.Precision.hasValue()) + Result.Precision = sizeof(void *) * 2; + + // This is the same workaround used above in the integer formatter. + if (isPrefixedHexStyle(Result.Style)) + *Result.Precision += 2; + return Result; + } + +public: + static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) { + Options S = parseStyle(Style); + write_hex(Stream, reinterpret_cast(V), S.Style, + S.Precision); + return; + } +}; + +/// \brief implementation of format_provider for c-style strings and string +/// objects such as std::string and llvm::StringRef. +/// +/// The options string has the following format: +/// +/// [N] +/// +/// where N is an optional integer specifying the maximum number of characters +/// in the string to print. If N is omitted, the string is printed up to the +/// null terminator. + +template +struct format_provider< + T, typename std::enable_if::value>::type> { + static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) { + size_t N = StringRef::npos; + if (!Style.empty() && Style.getAsInteger(10, N)) { + assert(false && "Style is not a valid integer"); + } + llvm::StringRef S(V); + Stream << S.substr(0, N); + } +}; + +/// \brief implementation of format_provider for characters. +/// +/// By default (if the option string is non-empty) the character is displayed +/// as an ASCII character. If the options string is non-empty, it is treated +/// as an integer and formatted using the integer formatter. +/// +template +struct format_provider< + T, typename std::enable_if::value>::type> { + static void format(const char &V, llvm::raw_ostream &Stream, + StringRef Style) { + if (Style.empty()) + Stream << V; + else { + int X = static_cast(V); + format_provider::format(X, Stream, Style); + } + } +}; + +/// \brief implementation of format_provider for floating point types. +/// +/// The options string of a floating point type has the format: +/// +/// [S][##] +/// +/// where S is an optional character sequence specifying the presentation style +/// of the number, and ## is an optional precision specifier of up to 2 decimal +/// digits which represents the precision. The default precision is always 2, +/// and represents the number of digits to display after the decimal. +/// +template +struct format_provider< + T, typename std::enable_if::value>::type> { + static void format(const T &V, llvm::raw_ostream &Stream, StringRef Style) { + FloatStyle S; + if (Style.consume_front("P") || Style.consume_front("p")) + S = FloatStyle::Percent; + else if (Style.consume_front("F") || Style.consume_front("f")) + S = FloatStyle::Fixed; + else if (Style.consume_front("E")) + S = FloatStyle::ExponentUpper; + else if (Style.consume_front("e")) + S = FloatStyle::Exponent; + else + S = FloatStyle::Fixed; + + Optional Precision = detail::parseNumericPrecision(Style); + if (!Precision.hasValue()) + Precision = getDefaultPrecision(S); + + write_double(Stream, static_cast(V), S, Precision); + } +}; +} + +#endif Index: include/llvm/Support/FormatVariadic.h =================================================================== --- /dev/null +++ include/llvm/Support/FormatVariadic.h @@ -0,0 +1,232 @@ +//===- FormatVariadic.h - Efficient type-safe string formatting --*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the format_string() function, which can be used with +// other LLVM subsystems to provide printf-like formatting, but with improved +// safety and flexibility. This can be used like this (with raw_ostreams as an +// example): +// +// OS << "result: " << format_string("{0} {1}", 1234.412, "test") << '\n'; +// +// Or if you prefer: +// +// OS << format_string("result: {0} {1}\n", 1234.412, "test"); +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FORMATVARIADIC_H +#define LLVM_SUPPORT_FORMATVARIADIC_H + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/Support/FormatProviders.h" +#include "llvm/Support/FormatVariadicDetails.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include + +namespace llvm { + +enum class ReplacementType { Empty, Format, Literal }; +enum class AlignStyle { Left, Center, Right }; + +struct ReplacementItem { + ReplacementItem() {} + explicit ReplacementItem(StringRef Literal) + : Type(ReplacementType::Literal), Spec(Literal) {} + ReplacementItem(StringRef Spec, size_t Index, size_t Align, AlignStyle Where, + StringRef Options) + : Type(ReplacementType::Format), Spec(Spec), Index(Index), Align(Align), + Where(Where), Options(Options) {} + ReplacementType Type = ReplacementType::Empty; + StringRef Spec; + size_t Index = 0; + size_t Align = 0; + AlignStyle Where = AlignStyle::Right; + StringRef Options; +}; + +class format_string_object_base { +protected: + struct create_wrappers { + template + std::vector operator()(Ts &... Items) { + return std::vector{&Items...}; + } + }; + + StringRef Fmt; + std::vector Wrappers; + std::vector Replacements; + + static std::pair + splitLiteralAndReplacement(StringRef Fmt); + static Optional parseReplacementItem(StringRef Spec); + +public: + format_string_object_base(StringRef Fmt, std::size_t ParamCount) + : Fmt(Fmt), Replacements(parseFormatString(Fmt)) { + Wrappers.reserve(ParamCount); + return; + } + + void format(raw_ostream &S) const { + for (auto &R : Replacements) { + if (R.Type == ReplacementType::Empty) + continue; + if (R.Type == ReplacementType::Literal) { + S << R.Spec; + continue; + } + if (R.Index >= Wrappers.size()) { + S << R.Spec; + continue; + } + + auto W = Wrappers[R.Index]; + + // If we don't need to align, we can format straight into the underlying + // stream. Otherwise we have to go through an intermediate stream first + // in order to calculate how long the output is so we can align it. + // TODO: Make the format method return the number of bytes written, that + // way we can also skip the intermediate stream for left-aligned output. + if (R.Align == 0) { + W->format(S, R.Options); + } else { + llvm::SmallString<32> Item; + llvm::raw_svector_ostream Stream(Item); + + W->format(Stream, R.Options); + if (R.Align <= Item.size()) { + S << Item; + } else { + size_t PadAmount = R.Align - Item.size(); + if (R.Where == AlignStyle::Left) { + S << Item; + S.indent(PadAmount); + } else if (R.Where == AlignStyle::Center) { + size_t X = PadAmount / 2; + S.indent(X); + S << Item; + S.indent(PadAmount - X); + } else { + S.indent(PadAmount); + S << Item; + } + } + } + } + } + static std::vector parseFormatString(StringRef Fmt); +}; + +template +class format_string_object : public format_string_object_base { + Tuple Parameters; + +public: + format_string_object(StringRef Fmt, Tuple &&Params) + : format_string_object_base(Fmt, std::tuple_size::value), + Parameters(std::move(Params)) { + Wrappers = apply_tuple(create_wrappers(), Parameters); + } +}; + +// Format output. `Fmt` is a string consisting of one or more replacement +// sequences whose format is as follows: +// +// {index[,alignment][:options]} +// +// - The zero-based index of the argument from the parameter pack +// to format. If this field is not present, the other fields +// are ignored and the replacement sequence is replaced with an +// empty string. +// alignment - (Optional) A string of the form: +// [s]n +// indicating the width of the field to print into, and the +// corresponding alignment. If `s` is the minus sign [-], the +// field is left aligned. If `s` is the equal sign [=], the field +// is center aligned. And if `s` is the plus sign [+], the field +// is right aligned. The default if `s` is missing is +. If the +// formatted text is longer than `alignment` then `alignment` is +// ignored and the replacement sequence is replaced with no +// leading or trailing spaces. +// options - A type-dependent string used to provide additional options to +// the formatting operation. Refer to the various implementations +// of format_provider for per-type options. +// +// Note that it is possible to refer to the same parameter index multiple +// times in a given format string. This makes it possible to output the same +// value multiple times without passing it multiple times to the function. +// For example: +// +// format_string("{0} {1} {0}", "a", "bb") +// +// would yield the string "abba". This can be convenient when it is expensive +// to compute the value of the parameter, and you would otherwise have had to +// save it to a temporary. +// +// For a given parameter of type T, the following steps are executed in order +// until a match is found: +// +// 1. If the parameter is of class type, and contains a method +// void format(SmallVectorImpl &Buffer, StringRef Options) +// Then this method is invoked to produce the formatted output. The +// implementation shoudl write the formatted text into `Buffer`. +// 2. If there is a suitable template specialization of format_provider<> +// for type T containing a method whose signature is: +// void format(const T &Obj, SmallVectorImpl &Buffer, +// StringRef Options) +// Then this method is invoked as described in Step 1. +// +// If a match cannot be found through any of the above methods, a compiler +// error is generated. + +template +inline auto format_string(const char *Fmt, Ts &&... Vals) + -> format_string_object(Vals))...))> { + using ParamTuple = decltype( + std::make_tuple(detail::build_format_wrapper(std::forward(Vals))...)); + return format_string_object( + Fmt, + std::make_tuple(detail::build_format_wrapper(std::forward(Vals))...)); +} + +class literal_format_obj { + const char *Fmt; + +public: + explicit literal_format_obj(const char *Fmt) : Fmt(Fmt) {} + + template std::string string(Ts &&... Vals) const { + std::string S; + llvm::raw_string_ostream Stream(S); + Stream << format_string(Fmt, std::forward(Vals)...); + Stream.flush(); + return S; + } + + template + auto stream(Ts &&... Vals) const + -> decltype(format_string("", std::forward(Vals)...)) { + return format_string(Fmt, std::forward(Vals)...); + } +}; + +inline literal_format_obj operator"" _fmt(const char *Fmt, std::size_t Size) { + return literal_format_obj(Fmt); +} + +} // end namespace llvm + +#endif Index: include/llvm/Support/FormatVariadicDetails.h =================================================================== --- /dev/null +++ include/llvm/Support/FormatVariadicDetails.h @@ -0,0 +1,130 @@ +//===- FormatVariadicDetails.h - Helpers for FormatVariadic.h ----*- C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_FORMATVARIADIC_DETAILS_H +#define LLVM_SUPPORT_FORMATVARIADIC_DETAILS_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +#include + +namespace llvm { +namespace detail { +class format_wrapper { +protected: + ~format_wrapper() {} + +public: + virtual void format(llvm::raw_ostream &S, StringRef Options) = 0; +}; + +template class member_format_wrapper : public format_wrapper { + T Item; + +public: + explicit member_format_wrapper(T &&Item) : Item(Item) {} + + void format(llvm::raw_ostream &S, StringRef Options) override { + Item.format(S, Options); + } +}; + +template class provider_format_wrapper : public format_wrapper { + T Item; + +public: + explicit provider_format_wrapper(T &&Item) : Item(Item) {} + + void format(llvm::raw_ostream &S, StringRef Options) override { + format_provider::type>::format(Item, S, Options); + } +}; + +template class missing_format_wrapper : public format_wrapper { +public: + missing_format_wrapper() { + static_assert(false, "T does not have a format_provider"); + } + void format(llvm::raw_ostream &S, StringRef Options) override {} +}; + +// Test if format(llvm::raw_ostream &S, StringRef Options) is +// defined on T. +template class has_FormatMember { +public: + static bool const value = false; +}; + +template +class has_FormatMember::value>::type> { + typedef void (*Signature_format)(llvm::raw_ostream &S, StringRef Options); + + template + static char test(SameType *); + + static double test(...); + +public: + static bool const value = (sizeof(test(nullptr)) == 1); +}; + +// Test if format_provider is defined on T. +template class has_FormatProvider { +public: + using Decayed = typename std::decay::type; + typedef void (*Signature_format)(const Decayed &, llvm::raw_ostream &, + StringRef); + + template + static char test(SameType *); + + template static double test(...); + + static bool const value = + (sizeof(test>(nullptr)) == 1); +}; + +template +struct uses_format_member + : public std::integral_constant::value> {}; +template +struct uses_format_provider + : public std::integral_constant::value && + has_FormatProvider::value> {}; +template +struct uses_missing_provider + : public std::integral_constant::value && + !has_FormatProvider::value> {}; + +template +typename std::enable_if::value, + member_format_wrapper>::type +build_format_wrapper(T &&Item) { + return member_format_wrapper(std::forward(Item)); +} + +template +typename std::enable_if::value, + provider_format_wrapper>::type +build_format_wrapper(T &&Item) { + return provider_format_wrapper(std::forward(Item)); +} + +template +typename std::enable_if::value, + missing_format_wrapper>::type +build_format_wrapper(T &&Item) { + return missing_format_wrapper(); +} +} +} + +#endif Index: include/llvm/Support/NativeFormatting.h =================================================================== --- include/llvm/Support/NativeFormatting.h +++ include/llvm/Support/NativeFormatting.h @@ -13,6 +13,7 @@ #include "llvm/ADT/Optional.h" #include "llvm/Support/raw_ostream.h" +#include #include namespace llvm { @@ -30,11 +31,18 @@ HexLowerNoPrefix }; enum class HexStyle { Upper, Lower, PrefixUpper, PrefixLower }; +enum class DurationStyle { Short, Long }; size_t getDefaultPrecision(FloatStyle Style); size_t getDefaultPrecision(IntegerStyle Style); size_t getDefaultPrecision(HexStyle Style); +HexStyle intHexStyleToHexStyle(IntegerStyle S); +IntegerStyle hexStyleToIntHexStyle(HexStyle S); +bool isHexStyle(IntegerStyle S); +bool isPrefixedHexStyle(IntegerStyle S); +bool isPrefixedHexStyle(HexStyle S); + void write_ulong(raw_ostream &S, unsigned long N, IntegerStyle Style, Optional Precision = None, Optional Width = None); void write_long(raw_ostream &S, long N, IntegerStyle Style, @@ -50,6 +58,35 @@ void write_double(raw_ostream &S, double D, FloatStyle Style, Optional Precision = None, Optional Width = None); + +template +typename std::enable_if::value, void>::type +write_integer(raw_ostream &S, T N, IntegerStyle Style, + Optional Precision = None, Optional Width = None) { + write_longlong(S, static_cast(N), Style, Precision, Width); +} + +template +typename std::enable_if::value, void>::type +write_integer(raw_ostream &S, T N, IntegerStyle Style, + Optional Precision = None, Optional Width = None) { + write_longlong(S, static_cast(N), Style, Precision, + Width); +} + +LLVM_ATTRIBUTE_ALWAYS_INLINE +void write_integer(raw_ostream &S, int64_t N, IntegerStyle Style, + Optional Precision = None, + Optional Width = None) { + write_longlong(S, N, Style, Precision, Width); +} + +LLVM_ATTRIBUTE_ALWAYS_INLINE +void write_integer(raw_ostream &S, uint64_t N, IntegerStyle Style, + Optional Precision = None, + Optional Width = None) { + write_ulonglong(S, N, Style, Precision, Width); +} } #endif \ No newline at end of file Index: include/llvm/Support/YAMLTraits.h =================================================================== --- include/llvm/Support/YAMLTraits.h +++ include/llvm/Support/YAMLTraits.h @@ -11,6 +11,7 @@ #define LLVM_SUPPORT_YAMLTRAITS_H #include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" @@ -201,10 +202,6 @@ // static T::value_type& element(IO &io, T &seq, size_t index); }; -// Only used by compiler if both template types are the same -template -struct SameType; - // Only used for better diagnostics of missing traits template struct MissingTrait; Index: include/llvm/Support/raw_ostream.h =================================================================== --- include/llvm/Support/raw_ostream.h +++ include/llvm/Support/raw_ostream.h @@ -20,6 +20,7 @@ #include namespace llvm { +class format_string_object_base; class format_object_base; class FormattedString; class FormattedNumber; @@ -222,6 +223,8 @@ // Formatted output, see the formatHex() function in Support/Format.h. raw_ostream &operator<<(const FormattedNumber &); + raw_ostream &operator<<(const format_string_object_base &); + /// indent - Insert 'NumSpaces' spaces. raw_ostream &indent(unsigned NumSpaces); Index: lib/Support/CMakeLists.txt =================================================================== --- lib/Support/CMakeLists.txt +++ lib/Support/CMakeLists.txt @@ -55,6 +55,7 @@ FileOutputBuffer.cpp FoldingSet.cpp FormattedStream.cpp + FormatVariadic.cpp GraphWriter.cpp Hashing.cpp IntEqClasses.cpp Index: lib/Support/FormatVariadic.cpp =================================================================== --- /dev/null +++ lib/Support/FormatVariadic.cpp @@ -0,0 +1,136 @@ +//===- FormatVariadic.cpp - Format string parsing and analysis ----*-C++-*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +//===----------------------------------------------------------------------===// + +#include "llvm/Support/FormatVariadic.h" + +using namespace llvm; + +Optional +format_string_object_base::parseReplacementItem(StringRef Spec) { + StringRef RepString = Spec.trim("{}"); + + std::size_t Commas = RepString.count(','); + std::size_t Colons = RepString.count(':'); + // Neither comma nor colon can appear more than once. If they do, this is + // not a valid spec, and we should just continue. + if (Commas > 1 || Colons > 1) + return None; + + size_t Index = 0; + std::size_t Align = 0; + AlignStyle Where = AlignStyle::Right; + StringRef Options; + std::size_t AlignPos = RepString.find_first_of(','); + std::size_t OptionsPos = RepString.find_first_of(':'); + + StringRef IndexStr = RepString.substr(0, std::min(AlignPos, OptionsPos)); + // An empty Index String ignores the alignment and options and replaces with + // a literal empty string. + if (IndexStr.empty()) + return ReplacementItem{}; + + StringRef AlignStr; + StringRef OptionsStr; + if (AlignPos > OptionsPos) { + AlignStr = RepString.slice(AlignPos, StringRef::npos); + OptionsStr = RepString.slice(OptionsPos, AlignPos); + } else { + AlignStr = RepString.slice(AlignPos, OptionsPos); + OptionsStr = RepString.slice(OptionsPos, StringRef::npos); + } + assert(AlignStr.empty() || AlignStr[0] == ',' && "Invalid AlignStr!"); + assert(OptionsStr.empty() || OptionsStr[0] == ':' && "Invalid OptionsStr!"); + + if (!AlignStr.empty()) + AlignStr = AlignStr.drop_front(); + if (!OptionsStr.empty()) + OptionsStr = OptionsStr.drop_front(); + AlignStr = AlignStr.trim(); + OptionsStr = OptionsStr.trim(); + IndexStr = IndexStr.trim(); + + if (!AlignStr.empty()) { + if (AlignStr.consume_front("-")) + Where = AlignStyle::Left; + else if (AlignStr.consume_front("=")) + Where = AlignStyle::Center; + else if (AlignStr.consume_front("+")) + Where = AlignStyle::Right; + + // If the alignment is not an unsigned integer, treat it as an invalid spec + // and continue. + if (AlignStr.getAsInteger(10, Align)) + return None; + } + if (IndexStr.getAsInteger(10, Index)) + return None; + return ReplacementItem{Spec, Index, Align, Where, OptionsStr}; +} + +std::pair +format_string_object_base::splitLiteralAndReplacement(StringRef Fmt) { + StringRef Rep; + StringRef Remainder; + std::size_t From = 0; + while (From < Fmt.size() && From != StringRef::npos) { + std::size_t BO = Fmt.find_first_of('{', From); + // Everything up until the first brace is a literal. + if (BO != 0) + return std::make_pair(ReplacementItem{Fmt.substr(0, BO)}, Fmt.substr(BO)); + + StringRef Braces = + Fmt.drop_front(BO).take_while([](char C) { return C == '{'; }); + // If there is more than one brace, then some of them are escaped. Treat + // these as replacements. + if (Braces.size() > 1) { + size_t NumEscapedBraces = Braces.size() / 2; + StringRef Middle = Fmt.substr(BO, NumEscapedBraces); + StringRef Right = Fmt.drop_front(BO + NumEscapedBraces * 2); + return std::make_pair(ReplacementItem{Middle}, Right); + } + // An open brace with no closing brace means there are no replacements, exit + // out. + std::size_t BC = Fmt.find_first_of('}', BO); + if (BC == StringRef::npos) + return std::make_pair(ReplacementItem{Fmt}, StringRef()); + + // Even if there is a closing brace, if there is another open brace before + // this + // closing brace, treat this portion as literal, and try again with the next + // one. + std::size_t BO2 = Fmt.find_first_of('{', BO + 1); + if (BO2 < BC) + return std::make_pair(ReplacementItem{Fmt.substr(0, BO2)}, + Fmt.substr(BO2)); + + StringRef Spec = Fmt.slice(BO + 1, BC); + StringRef Right = Fmt.substr(BC + 1); + + auto RI = parseReplacementItem(Spec); + if (RI.hasValue()) + return std::make_pair(*RI, Right); + + // If there was an error parsing the replacement item, treat it as an + // invalid + // replacement spec, and just continue. + From = BC + 1; + } + return std::make_pair(ReplacementItem{Fmt}, StringRef()); +} + +std::vector +format_string_object_base::parseFormatString(StringRef Fmt) { + std::vector Replacements; + ReplacementItem I; + while (!Fmt.empty()) { + std::tie(I, Fmt) = splitLiteralAndReplacement(Fmt); + if (I.Type != ReplacementType::Empty) + Replacements.push_back(I); + } + return Replacements; +} Index: lib/Support/NativeFormatting.cpp =================================================================== --- lib/Support/NativeFormatting.cpp +++ lib/Support/NativeFormatting.cpp @@ -16,35 +16,6 @@ using namespace llvm; -static bool isHexStyle(IntegerStyle S) { - switch (S) { - case IntegerStyle::HexLowerNoPrefix: - case IntegerStyle::HexLowerPrefix: - case IntegerStyle::HexUpperNoPrefix: - case IntegerStyle::HexUpperPrefix: - return true; - default: - return false; - } - LLVM_BUILTIN_UNREACHABLE; -} - -static HexStyle intHexStyleToHexStyle(IntegerStyle S) { - assert(isHexStyle(S)); - switch (S) { - case IntegerStyle::HexLowerNoPrefix: - return HexStyle::Lower; - case IntegerStyle::HexLowerPrefix: - return HexStyle::PrefixLower; - case IntegerStyle::HexUpperNoPrefix: - return HexStyle::Upper; - case IntegerStyle::HexUpperPrefix: - return HexStyle::PrefixUpper; - default: - break; - } - LLVM_BUILTIN_UNREACHABLE; -} static void writePadding(raw_ostream &S, Optional FieldWidth, size_t Chars) { @@ -98,12 +69,16 @@ static_assert(std::is_unsigned::value, "Value is not unsigned!"); if (Style == IntegerStyle::Exponent) { - write_double(S, static_cast(N), FloatStyle::Exponent, Precision, - Width); + double D = static_cast(N); + if (IsNegative) + D = -D; + write_double(S, D, FloatStyle::Exponent, Precision, Width); return; } else if (Style == IntegerStyle::ExponentUpper) { - write_double(S, static_cast(N), FloatStyle::ExponentUpper, - Precision, Width); + double D = static_cast(N); + if (IsNegative) + D = -D; + write_double(S, D, FloatStyle::ExponentUpper, Precision, Width); return; } else if (isHexStyle(Style)) { write_hex(S, N, intHexStyleToHexStyle(Style), Precision, Width); @@ -296,7 +271,9 @@ #if defined(__MINGW32__) // FIXME: It should be generic to C++11. if (N == 0.0 && std::signbit(N)) { - const char *NegativeZero = "-0.000000e+00"; + char NegativeZero[] = "-0.000000e+00"; + if (Style == FloatStyle::ExponentUpper) + NegativeZero[strlen(NegativeZero) - 4] = 'E'; writePadding(S, Width, strlen(NegativeZero)); S << NegativeZero; return; @@ -306,7 +283,9 @@ // negative zero if (fpcl == _FPCLASS_NZ) { - const char *NegativeZero = "-0.000000e+00"; + char NegativeZero[] = "-0.000000e+00"; + if (Style == FloatStyle::ExponentUpper) + NegativeZero[strlen(NegativeZero) - 4] = 'E'; writePadding(S, Width, strlen(NegativeZero)); S << NegativeZero; return; @@ -390,3 +369,69 @@ // Number of digits in the resulting string. return 0; } + +bool llvm::isPrefixedHexStyle(HexStyle S) { + switch (S) { + case HexStyle::PrefixLower: + case HexStyle::PrefixUpper: + return true; + default: + return false; + } + LLVM_BUILTIN_UNREACHABLE; +} + +bool llvm::isPrefixedHexStyle(IntegerStyle S) { + switch (S) { + case IntegerStyle::HexLowerPrefix: + case IntegerStyle::HexUpperPrefix: + return true; + default: + return false; + } + LLVM_BUILTIN_UNREACHABLE; +} + +bool llvm::isHexStyle(IntegerStyle S) { + switch (S) { + case IntegerStyle::HexLowerNoPrefix: + case IntegerStyle::HexLowerPrefix: + case IntegerStyle::HexUpperNoPrefix: + case IntegerStyle::HexUpperPrefix: + return true; + default: + return false; + } + LLVM_BUILTIN_UNREACHABLE; +} + +HexStyle llvm::intHexStyleToHexStyle(IntegerStyle S) { + assert(isHexStyle(S)); + switch (S) { + case IntegerStyle::HexLowerNoPrefix: + return HexStyle::Lower; + case IntegerStyle::HexLowerPrefix: + return HexStyle::PrefixLower; + case IntegerStyle::HexUpperNoPrefix: + return HexStyle::Upper; + case IntegerStyle::HexUpperPrefix: + return HexStyle::PrefixUpper; + default: + break; + } + LLVM_BUILTIN_UNREACHABLE; +} + +IntegerStyle llvm::hexStyleToIntHexStyle(HexStyle S) { + switch (S) { + case HexStyle::Lower: + return IntegerStyle::HexLowerNoPrefix; + case HexStyle::PrefixLower: + return IntegerStyle::HexLowerPrefix; + case HexStyle::Upper: + return IntegerStyle::HexUpperNoPrefix; + case HexStyle::PrefixUpper: + return IntegerStyle::HexUpperPrefix; + } + LLVM_BUILTIN_UNREACHABLE; +} Index: lib/Support/raw_ostream.cpp =================================================================== --- lib/Support/raw_ostream.cpp +++ lib/Support/raw_ostream.cpp @@ -20,6 +20,7 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/NativeFormatting.h" #include "llvm/Support/Process.h" @@ -318,6 +319,12 @@ } } +raw_ostream &raw_ostream::operator<<(const format_string_object_base &Obj) { + SmallString<128> S; + Obj.format(*this); + return *this; +} + raw_ostream &raw_ostream::operator<<(const FormattedString &FS) { unsigned Len = FS.Str.size(); int PadAmount = FS.Width - Len; Index: unittests/Support/CMakeLists.txt =================================================================== --- unittests/Support/CMakeLists.txt +++ unittests/Support/CMakeLists.txt @@ -19,6 +19,7 @@ ErrorTest.cpp ErrorOrTest.cpp FileOutputBufferTest.cpp + FormatVariadicTest.cpp LEB128Test.cpp LineIteratorTest.cpp LockFileManagerTest.cpp Index: unittests/Support/FormatVariadicTest.cpp =================================================================== --- /dev/null +++ unittests/Support/FormatVariadicTest.cpp @@ -0,0 +1,621 @@ +//===- FormatVariadicTest.cpp - Unit tests for string formatting ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/FormatVariadic.h" +#include "gtest/gtest.h" + +using namespace llvm; + +template +static std::string create_formatted_string(const char *Fmt, Ts &&... Args) { + std::string S; + llvm::raw_string_ostream OS(S); + OS << format_string(Fmt, std::forward(Args)...); + OS.flush(); + return S; +} + +TEST(FormatVariadicTest, EmptyFormatString) { + auto Replacements = format_string_object_base::parseFormatString(""); + EXPECT_EQ(0U, Replacements.size()); +} + +TEST(FormatVariadicTest, NoReplacements) { + const StringRef kFormatString = "This is a test"; + auto Replacements = + format_string_object_base::parseFormatString(kFormatString); + ASSERT_EQ(1U, Replacements.size()); + EXPECT_EQ(kFormatString, Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); +} + +TEST(FormatVariadicTest, EscapedBrace) { + // {{ should be replaced with { + auto Replacements = format_string_object_base::parseFormatString("{{"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + // An even number N of braces should be replaced with N/2 braces. + Replacements = format_string_object_base::parseFormatString("{{{{{{"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{{{", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + // An odd number 2*N+1 of braces should be replaced with N+1 braces when the + // last brace is not part of a replacement pattern. This will occur as two + // separate literal replacements. One for the even number, and one for the + // final one. + Replacements = format_string_object_base::parseFormatString("{{{{{{{"); + ASSERT_EQ(2u, Replacements.size()); + EXPECT_EQ("{{{", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + EXPECT_EQ("{", Replacements[1].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[1].Type); +} + +TEST(FormatVariadicTest, EmptyLiteralReplacement) { + auto Replacements = format_string_object_base::parseFormatString("{}"); + EXPECT_TRUE(Replacements.empty()); + + Replacements = format_string_object_base::parseFormatString("Test{}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("Test", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + Replacements = format_string_object_base::parseFormatString("{}Test{}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("Test", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + Replacements = format_string_object_base::parseFormatString("Te{}st"); + ASSERT_EQ(2u, Replacements.size()); + EXPECT_EQ("Te", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + EXPECT_EQ("st", Replacements[1].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[1].Type); +} + +TEST(FormatVariadicTest, ValidReplacementSequence) { + // 1. Simple replacement - parameter index only + auto Replacements = format_string_object_base::parseFormatString("{0}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(0, Replacements[0].Align); + EXPECT_EQ("", Replacements[0].Options); + + Replacements = format_string_object_base::parseFormatString("{1}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(1u, Replacements[0].Index); + EXPECT_EQ(0, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Right, Replacements[0].Where); + EXPECT_EQ("", Replacements[0].Options); + + // 2. Parameter index with right alignment + Replacements = format_string_object_base::parseFormatString("{0,3}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Right, Replacements[0].Where); + EXPECT_EQ("", Replacements[0].Options); + + // 3. And left alignment + Replacements = format_string_object_base::parseFormatString("{0,-3}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Left, Replacements[0].Where); + EXPECT_EQ("", Replacements[0].Options); + + // 4. And center alignment + Replacements = format_string_object_base::parseFormatString("{0,=3}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Center, Replacements[0].Where); + EXPECT_EQ("", Replacements[0].Options); + + // 4. Parameter index with option string + Replacements = format_string_object_base::parseFormatString("{0:foo}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(0, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Right, Replacements[0].Where); + EXPECT_EQ("foo", Replacements[0].Options); + + // 5. Parameter index with alignment before option string + Replacements = format_string_object_base::parseFormatString("{0,-3:foo}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Left, Replacements[0].Where); + EXPECT_EQ("foo", Replacements[0].Options); + + // 6. and option string before alignment + Replacements = format_string_object_base::parseFormatString("{0:foo,-3}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Left, Replacements[0].Where); + EXPECT_EQ("foo", Replacements[0].Options); + + // 7. Parameter indices, options, and alignment can all have whitespace. + Replacements = + format_string_object_base::parseFormatString("{ 0 : foo , -3 }"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Left, Replacements[0].Where); + EXPECT_EQ("foo", Replacements[0].Options); +} + +TEST(FormatVariadicTest, DefaultReplacementValues) { + // 1. If the index is empty, everything else is ignored. + auto Replacements = format_string_object_base::parseFormatString("{:foo,-3}"); + EXPECT_TRUE(Replacements.empty()); + + // 2. If options string is missing, it defaults to empty. + Replacements = format_string_object_base::parseFormatString("{0,3}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3, Replacements[0].Align); + EXPECT_EQ("", Replacements[0].Options); + + // Including if the colon is present but contains no text. + Replacements = format_string_object_base::parseFormatString("{0,3:}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(3, Replacements[0].Align); + EXPECT_EQ("", Replacements[0].Options); + + // 3. If alignment is missing, it defaults to 0. + Replacements = format_string_object_base::parseFormatString("{0:foo}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(0, Replacements[0].Align); + EXPECT_EQ("foo", Replacements[0].Options); + + // Including if the comma is present but the field value is missing. + Replacements = format_string_object_base::parseFormatString("{0:foo,}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(0, Replacements[0].Align); + EXPECT_EQ("foo", Replacements[0].Options); + + // If an empty replacement appears in the middle of another replacement, the + // empty one breaks the replacement. + Replacements = format_string_object_base::parseFormatString("{0:f{}oo,}"); + ASSERT_EQ(2u, Replacements.size()); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + EXPECT_EQ("{0:f", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[1].Type); + EXPECT_EQ("oo,}", Replacements[1].Spec); +} + +TEST(FormatVariadicTest, InvalidReplacementSequence) { + // 1. Parameter index cannot be signed. + auto Replacements = format_string_object_base::parseFormatString("{-1}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{-1}", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + // Or non-integral. + Replacements = format_string_object_base::parseFormatString("{0.1}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{0.1}", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + // Or contain alphabetic characters + Replacements = format_string_object_base::parseFormatString("{x}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{x}", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + // 2. Cannot contain multiple option specifiers + Replacements = format_string_object_base::parseFormatString("{:0:1}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{:0:1}", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + // Or alignment specifiers + Replacements = format_string_object_base::parseFormatString("{,0,1}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{,0,1}", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + // Or a non integer. + Replacements = format_string_object_base::parseFormatString("{0,0.1}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{0,0.1}", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + Replacements = format_string_object_base::parseFormatString("{0,x}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{0,x}", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + // 4. An open brace must be terminated by a close brace. + Replacements = format_string_object_base::parseFormatString("{0"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{0", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); + + // 5. An open brace between an open brace and close brace begins a new + // replacement sequence. + Replacements = format_string_object_base::parseFormatString("{0{}"); + ASSERT_EQ(1u, Replacements.size()); + EXPECT_EQ("{0", Replacements[0].Spec); + EXPECT_EQ(ReplacementType::Literal, Replacements[0].Type); +} + +TEST(FormatVariadicTest, MultipleReplacements) { + auto Replacements = + format_string_object_base::parseFormatString("{0} {1:foo}-{2:bar,-3}"); + ASSERT_EQ(5u, Replacements.size()); + // {0} + EXPECT_EQ(ReplacementType::Format, Replacements[0].Type); + EXPECT_EQ(0u, Replacements[0].Index); + EXPECT_EQ(0, Replacements[0].Align); + EXPECT_EQ(AlignStyle::Right, Replacements[0].Where); + EXPECT_EQ("", Replacements[0].Options); + + // " " + EXPECT_EQ(ReplacementType::Literal, Replacements[1].Type); + EXPECT_EQ(" ", Replacements[1].Spec); + + // {1:foo} - Options=foo + EXPECT_EQ(ReplacementType::Format, Replacements[2].Type); + EXPECT_EQ(1u, Replacements[2].Index); + EXPECT_EQ(0, Replacements[2].Align); + EXPECT_EQ(AlignStyle::Right, Replacements[2].Where); + EXPECT_EQ("foo", Replacements[2].Options); + + // "-" + EXPECT_EQ(ReplacementType::Literal, Replacements[3].Type); + EXPECT_EQ("-", Replacements[3].Spec); + + // {2:bar,-3} - Options=bar, Align=-3 + EXPECT_EQ(ReplacementType::Format, Replacements[4].Type); + EXPECT_EQ(2u, Replacements[4].Index); + EXPECT_EQ(3, Replacements[4].Align); + EXPECT_EQ(AlignStyle::Left, Replacements[4].Where); + EXPECT_EQ("bar", Replacements[4].Options); +} + +TEST(FormatVariadicTest, FormatNoReplacements) { + EXPECT_EQ("", create_formatted_string("")); + EXPECT_EQ("Test", create_formatted_string("Test")); +} + +TEST(FormatVariadicTest, FormatBasicTypesOneReplacement) { + EXPECT_EQ("1", create_formatted_string("{0}", 1)); + EXPECT_EQ("c", create_formatted_string("{0}", 'c')); + EXPECT_EQ("-3", create_formatted_string("{0}", -3)); + EXPECT_EQ("Test", create_formatted_string("{0}", "Test")); + EXPECT_EQ("Test2", create_formatted_string("{0}", StringRef("Test2"))); + EXPECT_EQ("Test3", create_formatted_string("{0}", std::string("Test3"))); +} + +TEST(FormatVariadicTest, IntegralHexFormatting) { + // 1. Trivial cases. Make sure hex is not the default. + EXPECT_EQ("0", create_formatted_string("{0}", 0)); + EXPECT_EQ("2748", create_formatted_string("{0}", 0xABC)); + EXPECT_EQ("-2748", create_formatted_string("{0}", -0xABC)); + + // 3. various hex prefixes. + EXPECT_EQ("0xFF", create_formatted_string("{0:X}", 255)); + EXPECT_EQ("0xFF", create_formatted_string("{0:X+}", 255)); + EXPECT_EQ("0xff", create_formatted_string("{0:x}", 255)); + EXPECT_EQ("0xff", create_formatted_string("{0:x+}", 255)); + EXPECT_EQ("FF", create_formatted_string("{0:X-}", 255)); + EXPECT_EQ("ff", create_formatted_string("{0:x-}", 255)); + + // 4. Signed numbers less than 0 also print in hex. + EXPECT_EQ("0xFFFFFFFFFFFFFFFF", create_formatted_string("{0:X}", -1)); + + // 5. Precision pads left of the most significant digit but right of the + // prefix (if one exists). + EXPECT_EQ("0xFF", create_formatted_string("{0:X2}", 255)); + EXPECT_EQ("0xFF", create_formatted_string("{0:X+2}", 255)); + EXPECT_EQ("0x0ff", create_formatted_string("{0:x3}", 255)); + EXPECT_EQ("0x0ff", create_formatted_string("{0:x+3}", 255)); + EXPECT_EQ("00FF", create_formatted_string("{0:X-4}", 255)); + EXPECT_EQ("00ff", create_formatted_string("{0:x-4}", 255)); + + // 6. Try some larger types. + EXPECT_EQ("0xDEADBEEFDEADBEEF", + create_formatted_string("{0:X16}", -2401053088876216593LL)); + EXPECT_EQ("0xFEEBDAEDFEEBDAED", + create_formatted_string("{0:X16}", 0xFEEBDAEDFEEBDAEDULL)); + EXPECT_EQ("0x00000000DEADBEEF", + create_formatted_string("{0:X16}", 0xDEADBEEF)); + + // 7. Padding should take into account the prefix + EXPECT_EQ("0xff", create_formatted_string("{0,4:x}", 255)); + EXPECT_EQ(" 0xff", create_formatted_string("{0,5:x+}", 255)); + EXPECT_EQ(" FF", create_formatted_string("{0,4:X-}", 255)); + EXPECT_EQ(" ff", create_formatted_string("{0,5:x-}", 255)); + + // 8. Including when it's been zero-padded + EXPECT_EQ(" 0x0ff", create_formatted_string("{0,7:x3}", 255)); + EXPECT_EQ(" 0x00ff", create_formatted_string("{0,7:x+4}", 255)); + EXPECT_EQ(" 000FF", create_formatted_string("{0,7:X-5}", 255)); + EXPECT_EQ(" 0000ff", create_formatted_string("{0,7:x-6}", 255)); + + // 9. Precision with default format specifier should work too + EXPECT_EQ(" 255", create_formatted_string("{0,7:3}", 255)); + EXPECT_EQ(" 0255", create_formatted_string("{0,7:4}", 255)); + EXPECT_EQ(" 00255", create_formatted_string("{0,7:5}", 255)); + EXPECT_EQ(" 000255", create_formatted_string("{0,7:6}", 255)); +} + +TEST(FormatVariadicTest, PointerFormatting) { + // 1. Trivial cases. Hex is default. Default Precision is pointer width. + if (sizeof(void *) == 4) { + EXPECT_EQ("0x00000000", create_formatted_string("{0}", (void *)0)); + EXPECT_EQ("0x00000ABC", create_formatted_string("{0}", (void *)0xABC)); + } else { + EXPECT_EQ("0x0000000000000000", create_formatted_string("{0}", (void *)0)); + EXPECT_EQ("0x0000000000000ABC", + create_formatted_string("{0}", (void *)0xABC)); + } + + // 2. But we can reduce the precision explicitly. + EXPECT_EQ("0x0", create_formatted_string("{0:0}", (void *)0)); + EXPECT_EQ("0xABC", create_formatted_string("{0:0}", (void *)0xABC)); + EXPECT_EQ("0x0000", create_formatted_string("{0:4}", (void *)0)); + EXPECT_EQ("0x0ABC", create_formatted_string("{0:4}", (void *)0xABC)); + + // 3. various hex prefixes. + EXPECT_EQ("0x0ABC", create_formatted_string("{0:X4}", (void *)0xABC)); + EXPECT_EQ("0x0abc", create_formatted_string("{0:x4}", (void *)0xABC)); + EXPECT_EQ("0ABC", create_formatted_string("{0:X-4}", (void *)0xABC)); + EXPECT_EQ("0abc", create_formatted_string("{0:x-4}", (void *)0xABC)); +} + +TEST(FormatVariadicTest, IntegralNumberFormatting) { + // 1. Test comma grouping with default widths and precisions. + EXPECT_EQ("0", create_formatted_string("{0:N}", 0)); + EXPECT_EQ("10", create_formatted_string("{0:N}", 10)); + EXPECT_EQ("100", create_formatted_string("{0:N}", 100)); + EXPECT_EQ("1,000", create_formatted_string("{0:N}", 1000)); + EXPECT_EQ("1,234,567,890", create_formatted_string("{0:N}", 1234567890)); + EXPECT_EQ("-10", create_formatted_string("{0:N}", -10)); + EXPECT_EQ("-100", create_formatted_string("{0:N}", -100)); + EXPECT_EQ("-1,000", create_formatted_string("{0:N}", -1000)); + EXPECT_EQ("-1,234,567,890", create_formatted_string("{0:N}", -1234567890)); + + // 2. If there is no comma, width and precision pad to the same absolute + // size. + EXPECT_EQ("01", create_formatted_string("{0:N2}", 1)); + EXPECT_EQ(" 1", create_formatted_string("{0,2:N}", 1)); + + // 3. But if there is a comma or negative sign, width factors them in but + // precision doesn't. + EXPECT_EQ("001,000", create_formatted_string("{0:N6}", 1000)); + EXPECT_EQ(" 1,000", create_formatted_string("{0,6:N}", 1000)); + EXPECT_EQ("-0,001,000", create_formatted_string("{0:N7}", -1000)); + EXPECT_EQ(" -1,000", create_formatted_string("{0,7:N}", -1000)); + + // 4. Large widths all line up. + EXPECT_EQ(" 1,000", create_formatted_string("{0,11:N}", 1000)); + EXPECT_EQ(" -1,000", create_formatted_string("{0,11:N}", -1000)); + EXPECT_EQ(" -100,000", create_formatted_string("{0,11:N}", -100000)); + EXPECT_EQ(" 0,001,000", create_formatted_string("{0,11:N7}", 1000)); +} + +TEST(FormatVariadicTest, IntegralPercentFormatting) { + EXPECT_EQ("0%", create_formatted_string("{0:P}", 0)); + EXPECT_EQ("0.00%", create_formatted_string("{0:P2}", 0)); + EXPECT_EQ(" 0.00%", create_formatted_string("{0,7:P2}", 0)); + + EXPECT_EQ(" 100.00%", create_formatted_string("{0,8:P2}", 1)); + + EXPECT_EQ(" 100%", create_formatted_string("{0,9:P}", 1)); + EXPECT_EQ(" 100.000%", create_formatted_string("{0,9:P3}", 1)); +} + +TEST(FormatVariadicTest, IntegralExponentFormatting) { + // 1. Try numbers 0 and non-zero exponents + EXPECT_EQ("4.230000E+02", create_formatted_string("{0:E}", 423)); + EXPECT_EQ("3.000000E+00", create_formatted_string("{0:E}", 3)); + + // 2. And with lowercase lowercase E + EXPECT_EQ("4.230000e+02", create_formatted_string("{0:e}", 423)); + EXPECT_EQ("3.000000e+00", create_formatted_string("{0:e}", 3)); + + // 3. And with negative numbers. + EXPECT_EQ("-4.230000e+02", create_formatted_string("{0:e}", -423)); + EXPECT_EQ("-3.000000e+00", create_formatted_string("{0:e}", -3)); + + // 4. And zero + EXPECT_EQ("0.000000e+00", create_formatted_string("{0:e}", 0)); + + // 5. Then try everything again with zero precision. + EXPECT_EQ("4E+02", create_formatted_string("{0:E0}", 423)); + EXPECT_EQ("3E+00", create_formatted_string("{0:E0}", 3)); + EXPECT_EQ("4e+02", create_formatted_string("{0:e0}", 423)); + EXPECT_EQ("3e+00", create_formatted_string("{0:e0}", 3)); + EXPECT_EQ("-4e+02", create_formatted_string("{0:e0}", -423)); + EXPECT_EQ("-3e+00", create_formatted_string("{0:e0}", -3)); + EXPECT_EQ("0e+00", create_formatted_string("{0:e0}", 0)); + + // 5. And with padding. + EXPECT_EQ(" 4.23E+02", create_formatted_string("{0,10:E2}", 423)); + EXPECT_EQ(" 3.00E+00", create_formatted_string("{0,10:E2}", 3)); + EXPECT_EQ(" 4.23e+02", create_formatted_string("{0,10:e2}", 423)); + EXPECT_EQ(" 3.00e+00", create_formatted_string("{0,10:e2}", 3)); + EXPECT_EQ(" -4.23e+02", create_formatted_string("{0,10:e2}", -423)); + EXPECT_EQ(" -3.00e+00", create_formatted_string("{0,10:e2}", -3)); + EXPECT_EQ(" 0.00e+00", create_formatted_string("{0,10:e2}", 0)); +} + +TEST(FormatVariadicTest, IntegralFixedFormatting) { + // Not much to see here. Just test that zero, positive, and negative values + // print with the requested number of decimal places. + EXPECT_EQ("423.00", create_formatted_string("{0:F}", 423)); + EXPECT_EQ("-423.00", create_formatted_string("{0:F}", -423)); + EXPECT_EQ("0.00", create_formatted_string("{0:F}", 0)); + + EXPECT_EQ("423.000", create_formatted_string("{0:F3}", 423)); + EXPECT_EQ("-423.000", create_formatted_string("{0:F3}", -423)); + EXPECT_EQ("0.000", create_formatted_string("{0:F3}", 0)); +} + +TEST(FormatVariadicTest, StringFormatting) { + const char FooArray[] = "FooArray"; + const char *FooPtr = "FooPtr"; + llvm::StringRef FooRef("FooRef"); + std::string FooString("FooString"); + // 1. Test that we can print various types of strings. + EXPECT_EQ(FooArray, create_formatted_string("{0}", FooArray)); + EXPECT_EQ(FooPtr, create_formatted_string("{0}", FooPtr)); + EXPECT_EQ(FooRef, create_formatted_string("{0}", FooRef)); + EXPECT_EQ(FooString, create_formatted_string("{0}", FooString)); + + // 2. Test that the precision specifier prints the correct number of + // characters. + EXPECT_EQ("FooA", create_formatted_string("{0:4}", FooArray)); + EXPECT_EQ("FooP", create_formatted_string("{0:4}", FooPtr)); + EXPECT_EQ("FooR", create_formatted_string("{0:4}", FooRef)); + EXPECT_EQ("FooS", create_formatted_string("{0:4}", FooString)); + + // 3. And that padding works. + EXPECT_EQ(" FooA", create_formatted_string("{0,6:4}", FooArray)); + EXPECT_EQ(" FooP", create_formatted_string("{0,6:4}", FooPtr)); + EXPECT_EQ(" FooR", create_formatted_string("{0,6:4}", FooRef)); + EXPECT_EQ(" FooS", create_formatted_string("{0,6:4}", FooString)); +} + +TEST(FormatVariadicTest, CharFormatting) { + // 1. Not much to see here. Just print a char with and without padding. + EXPECT_EQ("C", create_formatted_string("{0}", 'C')); + EXPECT_EQ(" C", create_formatted_string("{0,3}", 'C')); + + // 2. char is really an integral type though, where the only difference is + // that the "default" is to print the ASCII. So if a non-default presentation + // specifier exists, it should print as an integer. + EXPECT_EQ("37", create_formatted_string("{0:D}", (char)37)); + EXPECT_EQ(" 037", create_formatted_string("{0,5:D3}", (char)37)); +} + +TEST(FormatVariadicTest, DoubleFormatting) { + // Test exponents, fixed point, and percent formatting. + + // 1. Signed, unsigned, and zero exponent format. + EXPECT_EQ("0.000000E+00", create_formatted_string("{0:E}", 0.0)); + EXPECT_EQ("-0.000000E+00", create_formatted_string("{0:E}", -0.0)); + EXPECT_EQ("1.100000E+00", create_formatted_string("{0:E}", 1.1)); + EXPECT_EQ("-1.100000E+00", create_formatted_string("{0:E}", -1.1)); + EXPECT_EQ("1.234568E+03", create_formatted_string("{0:E}", 1234.5678)); + EXPECT_EQ("-1.234568E+03", create_formatted_string("{0:E}", -1234.5678)); + EXPECT_EQ("1.234568E-03", create_formatted_string("{0:E}", .0012345678)); + EXPECT_EQ("-1.234568E-03", create_formatted_string("{0:E}", -.0012345678)); + + // 2. With padding and precision. + EXPECT_EQ(" 0.000E+00", create_formatted_string("{0,11:E3}", 0.0)); + EXPECT_EQ(" -1.100E+00", create_formatted_string("{0,11:E3}", -1.1)); + EXPECT_EQ(" 1.235E+03", create_formatted_string("{0,11:E3}", 1234.5678)); + EXPECT_EQ(" -1.235E-03", create_formatted_string("{0,11:E3}", -.0012345678)); + + // 3. Signed, unsigned, and zero fixed point format. + EXPECT_EQ("0.00", create_formatted_string("{0:F}", 0.0)); + EXPECT_EQ("-0.00", create_formatted_string("{0:F}", -0.0)); + EXPECT_EQ("1.10", create_formatted_string("{0:F}", 1.1)); + EXPECT_EQ("-1.10", create_formatted_string("{0:F}", -1.1)); + EXPECT_EQ("1234.57", create_formatted_string("{0:F}", 1234.5678)); + EXPECT_EQ("-1234.57", create_formatted_string("{0:F}", -1234.5678)); + EXPECT_EQ("0.00", create_formatted_string("{0:F}", .0012345678)); + EXPECT_EQ("-0.00", create_formatted_string("{0:F}", -.0012345678)); + + // 2. With padding and precision. + EXPECT_EQ(" 0.000", create_formatted_string("{0,8:F3}", 0.0)); + EXPECT_EQ(" -1.100", create_formatted_string("{0,8:F3}", -1.1)); + EXPECT_EQ("1234.568", create_formatted_string("{0,8:F3}", 1234.5678)); + EXPECT_EQ(" -0.001", create_formatted_string("{0,8:F3}", -.0012345678)); +} + +struct format_tuple { + const char *Fmt; + explicit format_tuple(const char *Fmt) : Fmt(Fmt) {} + + template + auto operator()(Ts &&... Values) const + -> decltype(format_string(Fmt, std::forward(Values)...)) { + return format_string(Fmt, std::forward(Values)...); + } +}; + +TEST(FormatVariadicTest, BigTest) { + using Tuple = + std::tuple; + Tuple Ts[] = { + Tuple('a', 1, "Str", StringRef(), std::string(), 3.14159, -.17532f, + (void *)nullptr, 123456, 6.02E23, -908234908423, 908234908422234, + std::numeric_limits::quiet_NaN(), 0xAB), + Tuple('x', 0xDDB5B, "LongerStr", "StringRef", "std::string", -2.7, + .08215f, (void *)nullptr, 0, 6.62E-34, -908234908423, + 908234908422234, std::numeric_limits::infinity(), 0x0)}; + // Test long string formatting with many edge cases combined. + const char *Intro = + "There are {{{0}} items in the tuple, and {{{1}} tuple(s) in the array."; + const char *Header = + "{0,6}|{1,8}|{2,=10}|{3,=10}|{4,=13}|{5,7}|{6,7}|{7,10}|{8," + "-7}|{9,10}|{10,16}|{11,17}|{12,6}|{13,4}"; + const char *Line = + "{0,6}|{1,8:X}|{2,=10}|{3,=10:5}|{4,=13}|{5,7:3}|{6,7:P2}|{7," + "10:X8}|{8,-7:N}|{9,10:E4}|{10,16:N}|{11,17:D}|{12,6}|{13," + "4:X}"; + + std::string S; + llvm::raw_string_ostream Stream(S); + Stream << format_string(Intro, std::tuple_size::value, + llvm::array_lengthof(Ts)) + << "\n"; + Stream << format_string(Header, "Char", "HexInt", "Str", "Ref", "std::str", + "double", "float", "pointer", "comma", "exp", + "bigint", "bigint2", "limit", "byte") + << "\n"; + for (auto &Item : Ts) { + Stream << llvm::apply_tuple(format_tuple(Line), Item) << "\n"; + } + Stream.flush(); + const char *Expected = + R"foo(There are {14} items in the tuple, and {2} tuple(s) in the array. + Char| HexInt| Str | Ref | std::str | double| float| pointer|comma | exp| bigint| bigint2| limit|byte + a| 0x1| Str | | | 3.142|-17.53%|0x00000000|123,456|6.0200E+23|-908,234,908,423| 908234908422234| nan|0xAB + x| 0xDDB5B|LongerStr | Strin | std::string | -2.700| 8.21%|0x00000000|0 |6.6200E-34|-908,234,908,423| 908234908422234| INF| 0x0 +)foo"; + + EXPECT_EQ(Expected, S); +} + +TEST(FormatVariadicTest, UDL) { + // double x = 90.0_deg; + EXPECT_EQ(" Test ", "{0,=8}"_fmt.string("Test")); + + llvm::SmallString<32> TestStr; + llvm::raw_svector_ostream Stream(TestStr); + Stream << "{0,=8}"_fmt.stream("Test"); + EXPECT_EQ(" Test ", TestStr); +} \ No newline at end of file