brintos

brintos / llvm-project-archived public Read only

0
0
Text · 17.1 KiB · cfcd08b Raw
547 lines · cpp
1//===-- lib/Parser/message.cpp --------------------------------------------===//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 9#include "flang/Parser/message.h"10#include "flang/Common/idioms.h"11#include "flang/Parser/char-set.h"12#include "llvm/Support/raw_ostream.h"13#include <algorithm>14#include <cstdarg>15#include <cstddef>16#include <cstdio>17#include <cstring>18#include <string>19#include <tuple>20#include <vector>21 22namespace Fortran::parser {23 24// The nextCh parser emits this, and Message::GetProvenanceRange() looks for it.25const MessageFixedText MessageFixedText::endOfFileMessage{26    "end of file"_err_en_US};27 28llvm::raw_ostream &operator<<(llvm::raw_ostream &o, const MessageFixedText &t) {29  std::size_t n{t.text().size()};30  for (std::size_t j{0}; j < n; ++j) {31    o << t.text()[j];32  }33  return o;34}35 36void MessageFormattedText::Format(const MessageFixedText *text, ...) {37  const char *p{text->text().begin()};38  std::string asString;39  if (*text->text().end() != '\0') {40    // not NUL-terminated41    asString = text->text().NULTerminatedToString();42    p = asString.c_str();43  }44  va_list ap;45  va_start(ap, text);46#ifdef _MSC_VER47  // Microsoft has a separate function for "positional arguments", which is48  // used in some messages.49  int need{_vsprintf_p(nullptr, 0, p, ap)};50#else51  int need{vsnprintf(nullptr, 0, p, ap)};52#endif53 54  CHECK(need >= 0);55  char *buffer{56      static_cast<char *>(std::malloc(static_cast<std::size_t>(need) + 1))};57  CHECK(buffer);58  va_end(ap);59  va_start(ap, text);60#ifdef _MSC_VER61  // Use positional argument variant of printf.62  int need2{_vsprintf_p(buffer, need + 1, p, ap)};63#else64  int need2{vsnprintf(buffer, need + 1, p, ap)};65#endif66  CHECK(need2 == need);67  va_end(ap);68  string_ = buffer;69  std::free(buffer);70  conversions_.clear();71}72 73const char *MessageFormattedText::Convert(const std::string &s) {74  conversions_.emplace_front(s);75  return conversions_.front().c_str();76}77 78const char *MessageFormattedText::Convert(std::string &&s) {79  conversions_.emplace_front(std::move(s));80  return conversions_.front().c_str();81}82 83const char *MessageFormattedText::Convert(const std::string_view &s) {84  conversions_.emplace_front(s);85  return conversions_.front().c_str();86}87 88const char *MessageFormattedText::Convert(std::string_view &&s) {89  conversions_.emplace_front(s);90  return conversions_.front().c_str();91}92 93const char *MessageFormattedText::Convert(CharBlock x) {94  return Convert(x.ToString());95}96 97std::string MessageExpectedText::ToString() const {98  return common::visit(99      common::visitors{100          [](CharBlock cb) {101            return MessageFormattedText("expected '%s'"_err_en_US, cb)102                .MoveString();103          },104          [](const SetOfChars &set) {105            SetOfChars expect{set};106            if (expect.Has('\n')) {107              expect = expect.Difference('\n');108              if (expect.empty()) {109                return "expected end of line"_err_en_US.text().ToString();110              } else {111                std::string s{expect.ToString()};112                if (s.size() == 1) {113                  return MessageFormattedText(114                      "expected end of line or '%s'"_err_en_US, s)115                      .MoveString();116                } else {117                  return MessageFormattedText(118                      "expected end of line or one of '%s'"_err_en_US, s)119                      .MoveString();120                }121              }122            }123            std::string s{expect.ToString()};124            if (s.size() != 1) {125              return MessageFormattedText("expected one of '%s'"_err_en_US, s)126                  .MoveString();127            } else {128              return MessageFormattedText("expected '%s'"_err_en_US, s)129                  .MoveString();130            }131          },132      },133      u_);134}135 136bool MessageExpectedText::Merge(const MessageExpectedText &that) {137  return common::visit(common::visitors{138                           [](SetOfChars &s1, const SetOfChars &s2) {139                             s1 = s1.Union(s2);140                             return true;141                           },142                           [](const auto &, const auto &) { return false; },143                       },144      u_, that.u_);145}146 147bool Message::SortBefore(const Message &that) const {148  // Messages from prescanning have ProvenanceRange values for their locations,149  // while messages from later phases have CharBlock values, since the150  // conversion of cooked source stream locations to provenances is not151  // free and needs to be deferred, and many messages created during parsing152  // are speculative.  Messages with ProvenanceRange locations are ordered153  // before others for sorting.154  return common::visit(155      common::visitors{156          [](CharBlock cb1, CharBlock cb2) {157            return cb1.begin() < cb2.begin();158          },159          [](CharBlock, const ProvenanceRange &) { return false; },160          [](const ProvenanceRange &pr1, const ProvenanceRange &pr2) {161            return pr1.start() < pr2.start();162          },163          [](const ProvenanceRange &, CharBlock) { return true; },164      },165      location_, that.location_);166}167 168bool Message::IsFatal() const {169  return severity() == Severity::Error || severity() == Severity::Todo;170}171 172Severity Message::severity() const {173  return common::visit(174      common::visitors{175          [](const MessageExpectedText &) { return Severity::Error; },176          [](const MessageFixedText &x) { return x.severity(); },177          [](const MessageFormattedText &x) { return x.severity(); },178      },179      text_);180}181 182Message &Message::set_severity(Severity severity) {183  common::visit(184      common::visitors{185          [](const MessageExpectedText &) {},186          [severity](MessageFixedText &x) { x.set_severity(severity); },187          [severity](MessageFormattedText &x) { x.set_severity(severity); },188      },189      text_);190  return *this;191}192 193std::optional<common::LanguageFeature> Message::languageFeature() const {194  return languageFeature_;195}196 197Message &Message::set_languageFeature(common::LanguageFeature feature) {198  languageFeature_ = feature;199  return *this;200}201 202std::optional<common::UsageWarning> Message::usageWarning() const {203  return usageWarning_;204}205 206Message &Message::set_usageWarning(common::UsageWarning warning) {207  usageWarning_ = warning;208  return *this;209}210 211std::string Message::ToString() const {212  return common::visit(213      common::visitors{214          [](const MessageFixedText &t) {215            return t.text().NULTerminatedToString();216          },217          [](const MessageFormattedText &t) { return t.string(); },218          [](const MessageExpectedText &e) { return e.ToString(); },219      },220      text_);221}222 223void Message::ResolveProvenances(const AllCookedSources &allCooked) {224  if (CharBlock * cb{std::get_if<CharBlock>(&location_)}) {225    if (std::optional<ProvenanceRange> resolved{226            allCooked.GetProvenanceRange(*cb)}) {227      location_ = *resolved;228    }229  }230  if (Message * attachment{attachment_.get()}) {231    attachment->ResolveProvenances(allCooked);232  }233}234 235std::optional<ProvenanceRange> Message::GetProvenanceRange(236    const AllCookedSources &allCooked) const {237  return common::visit(238      common::visitors{239          [&](CharBlock cb) -> std::optional<ProvenanceRange> {240            if (auto pr{allCooked.GetProvenanceRange(cb)}) {241              return pr;242            } else if (const auto *fixed{std::get_if<MessageFixedText>(&text_)};243                fixed &&244                fixed->text() == MessageFixedText::endOfFileMessage.text() &&245                cb.begin() && cb.size() == 1) {246              // Failure from "nextCh" due to reaching EOF.  Back up one byte247              // to the terminal newline so that the output looks better.248              return allCooked.GetProvenanceRange(CharBlock{cb.begin() - 1, 1});249            } else {250              return std::nullopt;251            }252          },253          [](const ProvenanceRange &pr) { return std::make_optional(pr); },254      },255      location_);256}257 258static std::string Prefix(Severity severity) {259  switch (severity) {260  case Severity::Error:261    return "error: ";262  case Severity::Warning:263    return "warning: ";264  case Severity::Portability:265    return "portability: ";266  case Severity::Because:267    return "because: ";268  case Severity::Context:269    return "in the context: ";270  case Severity::Todo:271    return "error: not yet implemented: ";272  case Severity::None:273    break;274  }275  return "";276}277 278static llvm::raw_ostream::Colors PrefixColor(Severity severity) {279  switch (severity) {280  case Severity::Error:281  case Severity::Todo:282    return llvm::raw_ostream::RED;283  case Severity::Warning:284  case Severity::Portability:285    return llvm::raw_ostream::MAGENTA;286  default:287    // TODO: Set the color.288    break;289  }290  return llvm::raw_ostream::SAVEDCOLOR;291}292 293static std::string HintLanguageControlFlag(294    const common::LanguageFeatureControl *hintFlagPtr,295    std::optional<common::LanguageFeature> feature,296    std::optional<common::UsageWarning> warning) {297  if (hintFlagPtr) {298    std::string flag;299    if (warning) {300      flag = hintFlagPtr->getDefaultCliSpelling(*warning);301    } else if (feature) {302      flag = hintFlagPtr->getDefaultCliSpelling(*feature);303    }304    if (!flag.empty()) {305      return " [-W" + flag + "]";306    }307  }308  return "";309}310 311static constexpr int MAX_CONTEXTS_EMITTED{2};312static constexpr bool OMIT_SHARED_CONTEXTS{true};313 314void Message::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,315    bool echoSourceLine,316    const common::LanguageFeatureControl *hintFlagPtr) const {317  std::optional<ProvenanceRange> provenanceRange{GetProvenanceRange(allCooked)};318  const AllSources &sources{allCooked.allSources()};319  const std::string text{ToString()};320  const std::string hint{321      HintLanguageControlFlag(hintFlagPtr, languageFeature_, usageWarning_)};322  sources.EmitMessage(o, provenanceRange, text + hint, Prefix(severity()),323      PrefixColor(severity()), echoSourceLine);324  // Refers to whether the attachment in the loop below is a context, but can't325  // be declared inside the loop because the previous iteration's326  // attachment->attachmentIsContext_ indicates this.327  bool isContext{attachmentIsContext_};328  int contextsEmitted{0};329  // Emit attachments.330  for (const Message *attachment{attachment_.get()}; attachment;331      isContext = attachment->attachmentIsContext_,332      attachment = attachment->attachment_.get()) {333    Severity severity = isContext ? Severity::Context : attachment->severity();334    auto emitAttachment = [&]() {335      sources.EmitMessage(o, attachment->GetProvenanceRange(allCooked),336          attachment->ToString(), Prefix(severity), PrefixColor(severity),337          echoSourceLine);338    };339 340    if (isContext) {341      // Truncate the number of contexts emitted.342      if (contextsEmitted < MAX_CONTEXTS_EMITTED) {343        emitAttachment();344        ++contextsEmitted;345      }346      if constexpr (OMIT_SHARED_CONTEXTS) {347        // Skip less specific contexts at the same location.348        for (const Message *next_attachment{attachment->attachment_.get()};349            next_attachment && next_attachment->attachmentIsContext_ &&350            next_attachment->AtSameLocation(*attachment);351            next_attachment = next_attachment->attachment_.get()) {352          attachment = next_attachment;353        }354        // NB, this loop increments `attachment` one more time after the355        // previous loop is done advancing it to the last context at the same356        // location.357      }358    } else {359      emitAttachment();360    }361  }362}363 364// Messages are equal if they're for the same location and text, and the user365// visible aspects of their attachments are the same366bool Message::operator==(const Message &that) const {367  if (!AtSameLocation(that) || ToString() != that.ToString() ||368      severity() != that.severity() ||369      attachmentIsContext_ != that.attachmentIsContext_) {370    return false;371  }372  const Message *thatAttachment{that.attachment_.get()};373  for (const Message *attachment{attachment_.get()}; attachment;374      attachment = attachment->attachment_.get()) {375    if (!thatAttachment || !attachment->AtSameLocation(*thatAttachment) ||376        attachment->ToString() != thatAttachment->ToString() ||377        attachment->severity() != thatAttachment->severity()) {378      return false;379    }380    thatAttachment = thatAttachment->attachment_.get();381  }382  return !thatAttachment;383}384 385bool Message::Merge(const Message &that) {386  return AtSameLocation(that) &&387      (!that.attachment_.get() ||388          attachment_.get() == that.attachment_.get()) &&389      common::visit(390          common::visitors{391              [](MessageExpectedText &e1, const MessageExpectedText &e2) {392                return e1.Merge(e2);393              },394              [](const auto &, const auto &) { return false; },395          },396          text_, that.text_);397}398 399Message &Message::Attach(Message *m) {400  if (!attachment_) {401    attachment_ = m;402  } else {403    if (attachment_->references() > 1) {404      // Don't attach to a shared context attachment; copy it first.405      attachment_ = new Message{*attachment_};406    }407    attachment_->Attach(m);408  }409  return *this;410}411 412Message &Message::Attach(std::unique_ptr<Message> &&m) {413  return Attach(m.release());414}415 416bool Message::AtSameLocation(const Message &that) const {417  return common::visit(418      common::visitors{419          [](CharBlock cb1, CharBlock cb2) {420            return cb1.begin() == cb2.begin();421          },422          [](const ProvenanceRange &pr1, const ProvenanceRange &pr2) {423            return pr1.start() == pr2.start();424          },425          [](const auto &, const auto &) { return false; },426      },427      location_, that.location_);428}429 430bool Messages::Merge(const Message &msg) {431  if (msg.IsMergeable()) {432    for (auto &m : messages_) {433      if (m.Merge(msg)) {434        return true;435      }436    }437  }438  return false;439}440 441void Messages::Merge(Messages &&that) {442  if (messages_.empty()) {443    *this = std::move(that);444  } else {445    while (!that.messages_.empty()) {446      if (Merge(that.messages_.front())) {447        that.messages_.pop_front();448      } else {449        auto next{that.messages_.begin()};450        ++next;451        messages_.splice(452            messages_.end(), that.messages_, that.messages_.begin(), next);453      }454    }455  }456}457 458void Messages::Copy(const Messages &that) {459  for (const Message &m : that.messages_) {460    Message copy{m};461    Say(std::move(copy));462  }463}464 465void Messages::ResolveProvenances(const AllCookedSources &allCooked) {466  for (Message &m : messages_) {467    m.ResolveProvenances(allCooked);468  }469}470 471void Messages::Emit(llvm::raw_ostream &o, const AllCookedSources &allCooked,472    bool echoSourceLines, const common::LanguageFeatureControl *hintFlagPtr,473    std::size_t maxErrorsToEmit, bool warningsAreErrors) const {474  std::vector<const Message *> sorted;475  for (const auto &msg : messages_) {476    sorted.push_back(&msg);477  }478  std::stable_sort(sorted.begin(), sorted.end(),479      [](const Message *x, const Message *y) { return x->SortBefore(*y); });480  std::vector<const Message *> msgsWithLastLocation;481  std::size_t errorsEmitted{0};482  for (const Message *msg : sorted) {483    bool shouldSkipMsg{false};484    // Don't emit two identical messages for the same location.485    // At the same location, messages are sorted by the order they were486    // added to the Messages buffer, which is a decent proxy for the487    // causality of the messages.488    if (!msgsWithLastLocation.empty()) {489      if (msgsWithLastLocation[0]->AtSameLocation(*msg)) {490        for (const Message *msgAtThisLocation : msgsWithLastLocation) {491          if (*msg == *msgAtThisLocation) {492            shouldSkipMsg = true; // continue loop over sorted messages493            break;494          }495        }496      } else {497        msgsWithLastLocation.clear();498      }499    }500    if (shouldSkipMsg) {501      continue;502    }503    msgsWithLastLocation.push_back(msg);504    msg->Emit(o, allCooked, echoSourceLines, hintFlagPtr);505    if (warningsAreErrors || msg->IsFatal()) {506      ++errorsEmitted;507    }508    // If maxErrorsToEmit is 0, emit all errors, otherwise break after509    // maxErrorsToEmit.510    if (maxErrorsToEmit > 0 && errorsEmitted >= maxErrorsToEmit) {511      break;512    }513  }514}515 516void Messages::AttachTo(Message &msg, std::optional<Severity> severity) {517  for (Message &m : messages_) {518    Message m2{std::move(m)};519    if (severity) {520      m2.set_severity(*severity);521    }522    msg.Attach(std::move(m2));523  }524  messages_.clear();525}526 527bool Messages::AnyFatalError(bool warningsAreErrors) const {528  // Short-circuit in the most common case.529  if (messages_.empty()) {530    return false;531  }532  // If warnings are errors and there are warnings or errors, this is fatal.533  // This preserves the compiler's current behavior of treating any non-fatal534  // message as a warning. We may want to refine this in the future.535  if (warningsAreErrors) {536    return true;537  }538  // Otherwise, check the message buffer for fatal errors.539  for (const auto &msg : messages_) {540    if (msg.IsFatal()) {541      return true;542    }543  }544  return false;545}546} // namespace Fortran::parser547