1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
//===- FormatVariadic.cpp - Format string parsing and analysis ----*-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
//===----------------------------------------------------------------------===//
#include "llvm/Support/FormatVariadic.h"
#include <cassert>
#include <optional>
using namespace llvm;
static std::optional<AlignStyle> translateLocChar(char C) {
switch (C) {
case '-':
return AlignStyle::Left;
case '=':
return AlignStyle::Center;
case '+':
return AlignStyle::Right;
default:
return std::nullopt;
}
LLVM_BUILTIN_UNREACHABLE;
}
static bool consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
unsigned &Align, char &Pad) {
Where = AlignStyle::Right;
Align = 0;
Pad = ' ';
if (Spec.empty())
return true;
if (Spec.size() > 1) {
// A maximum of 2 characters at the beginning can be used for something
// other than the width.
// If Spec[1] is a loc char, then Spec[0] is a pad char and Spec[2:...]
// contains the width.
// Otherwise, if Spec[0] is a loc char, then Spec[1:...] contains the width.
// Otherwise, Spec[0:...] contains the width.
if (auto Loc = translateLocChar(Spec[1])) {
Pad = Spec[0];
Where = *Loc;
Spec = Spec.drop_front(2);
} else if (auto Loc = translateLocChar(Spec[0])) {
Where = *Loc;
Spec = Spec.drop_front(1);
}
}
bool Failed = Spec.consumeInteger(0, Align);
return !Failed;
}
static std::optional<ReplacementItem> parseReplacementItem(StringRef Spec) {
StringRef RepString = Spec.trim("{}");
// If the replacement sequence does not start with a non-negative integer,
// this is an error.
char Pad = ' ';
unsigned Align = 0;
AlignStyle Where = AlignStyle::Right;
StringRef Options;
unsigned Index = ~0U;
RepString = RepString.ltrim();
// If index is not specified, keep it ~0U to indicate unresolved index.
RepString.consumeInteger(0, Index);
if (RepString.consume_front(",")) {
if (!consumeFieldLayout(RepString, Where, Align, Pad)) {
assert(false && "Invalid replacement field layout specification!");
return std::nullopt;
}
}
RepString = RepString.ltrim();
if (RepString.consume_front(":")) {
Options = RepString;
RepString = StringRef();
}
RepString = RepString.trim();
if (!RepString.empty()) {
assert(0 && "Unexpected characters found in replacement string!");
return std::nullopt;
}
return ReplacementItem(Spec, Index, Align, Where, Pad, Options);
}
static std::pair<std::optional<ReplacementItem>, StringRef>
splitLiteralAndReplacement(StringRef Fmt) {
assert(!Fmt.empty());
// Everything up until the first brace is a literal.
if (Fmt.front() != '{') {
size_t BO = Fmt.find_first_of('{');
return {ReplacementItem{Fmt.substr(0, BO)}, Fmt.substr(BO)};
}
StringRef Braces = Fmt.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.take_front(NumEscapedBraces);
StringRef Right = Fmt.drop_front(NumEscapedBraces * 2);
return {ReplacementItem(Middle), Right};
}
// An unterminated open brace is undefined. Assert to indicate that this is
// undefined and that we consider it an error. When asserts are disabled,
// build a replacement item with an error message.
size_t BC = Fmt.find_first_of('}');
if (BC == StringRef::npos) {
assert(false &&
"Unterminated brace sequence. Escape with {{ for a literal brace.");
return {ReplacementItem("Unterminated brace sequence. Escape with {{ for a "
"literal brace."),
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.
size_t BO2 = Fmt.find_first_of('{', 1);
if (BO2 < BC)
return {ReplacementItem(Fmt.substr(0, BO2)), Fmt.substr(BO2)};
StringRef Spec = Fmt.slice(1, BC);
StringRef Right = Fmt.substr(BC + 1);
return {parseReplacementItem(Spec), Right};
}
#ifndef NDEBUG
#define ENABLE_VALIDATION 1
#else
#define ENABLE_VALIDATION 0 // Conveniently enable validation in release mode.
#endif
SmallVector<ReplacementItem, 2>
formatv_object_base::parseFormatString(StringRef Fmt, size_t NumArgs,
bool Validate) {
SmallVector<ReplacementItem, 2> Replacements;
unsigned NextAutomaticIndex = 0;
#if ENABLE_VALIDATION
const StringRef SavedFmtStr = Fmt;
unsigned NumExpectedArgs = 0;
bool HasExplicitIndex = false;
#endif
while (!Fmt.empty()) {
std::optional<ReplacementItem> I;
std::tie(I, Fmt) = splitLiteralAndReplacement(Fmt);
if (!I)
continue;
if (I->Type == ReplacementType::Format) {
if (I->Index == ~0U)
I->Index = NextAutomaticIndex++;
#if ENABLE_VALIDATION
else
HasExplicitIndex = true;
NumExpectedArgs = std::max(NumExpectedArgs, I->Index + 1);
#endif
}
Replacements.emplace_back(*I);
}
#if ENABLE_VALIDATION
if (!Validate)
return Replacements;
// Perform additional validation. Verify that the number of arguments matches
// the number of replacement indices and that there are no holes in the
// replacement indices.
// When validation fails, return an array of replacement items that
// will print an error message as the outout of this formatv() (used when
// validation is enabled in release mode).
auto getErrorReplacements = [SavedFmtStr](StringLiteral ErrorMsg) {
return SmallVector<ReplacementItem, 2>{
ReplacementItem("Invalid formatv() call: "), ReplacementItem(ErrorMsg),
ReplacementItem(" for format string: "), ReplacementItem(SavedFmtStr)};
};
if (NumExpectedArgs != NumArgs) {
errs() << formatv("Expected {} Args, but got {} for format string '{}'\n",
NumExpectedArgs, NumArgs, SavedFmtStr);
assert(0 && "Invalid formatv() call");
return getErrorReplacements("Unexpected number of arguments");
}
// Find the number of unique indices seen. All replacement indices
// are < NumExpectedArgs.
SmallVector<bool> Indices(NumExpectedArgs);
unsigned Count = 0;
for (const ReplacementItem &I : Replacements) {
if (I.Type != ReplacementType::Format || Indices[I.Index])
continue;
Indices[I.Index] = true;
++Count;
}
if (Count != NumExpectedArgs) {
errs() << formatv(
"Replacement field indices cannot have holes for format string '{}'\n",
SavedFmtStr);
assert(0 && "Invalid format string");
return getErrorReplacements("Replacement indices have holes");
}
// Fail validation if we see both automatic index and explicit index.
if (NextAutomaticIndex != 0 && HasExplicitIndex) {
errs() << formatv(
"Cannot mix automatic and explicit indices for format string '{}'\n",
SavedFmtStr);
assert(0 && "Invalid format string");
return getErrorReplacements("Cannot mix automatic and explicit indices");
}
#endif // ENABLE_VALIDATION
return Replacements;
}
void support::detail::format_adapter::anchor() {}
|