brintos

brintos / llvm-project-archived public Read only

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