Home | History | Annotate | Download | only in gn
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "tools/gn/err.h"
      6 
      7 #include "base/strings/string_number_conversions.h"
      8 #include "base/strings/string_util.h"
      9 #include "tools/gn/filesystem_utils.h"
     10 #include "tools/gn/input_file.h"
     11 #include "tools/gn/parse_tree.h"
     12 #include "tools/gn/standard_out.h"
     13 #include "tools/gn/tokenizer.h"
     14 #include "tools/gn/value.h"
     15 
     16 namespace {
     17 
     18 std::string GetNthLine(const base::StringPiece& data, int n) {
     19   size_t line_off = Tokenizer::ByteOffsetOfNthLine(data, n);
     20   size_t end = line_off + 1;
     21   while (end < data.size() && !Tokenizer::IsNewline(data, end))
     22     end++;
     23   return data.substr(line_off, end - line_off).as_string();
     24 }
     25 
     26 void FillRangeOnLine(const LocationRange& range, int line_number,
     27                      std::string* line) {
     28   // Only bother if the range's begin or end overlaps the line. If the entire
     29   // line is highlighted as a result of this range, it's not very helpful.
     30   if (range.begin().line_number() != line_number &&
     31       range.end().line_number() != line_number)
     32     return;
     33 
     34   // Watch out, the char offsets in the location are 1-based, so we have to
     35   // subtract 1.
     36   int begin_char;
     37   if (range.begin().line_number() < line_number)
     38     begin_char = 0;
     39   else
     40     begin_char = range.begin().char_offset() - 1;
     41 
     42   int end_char;
     43   if (range.end().line_number() > line_number)
     44     end_char = static_cast<int>(line->size());  // Ending is non-inclusive.
     45   else
     46     end_char = range.end().char_offset() - 1;
     47 
     48   CHECK(end_char >= begin_char);
     49   CHECK(begin_char >= 0 && begin_char <= static_cast<int>(line->size()));
     50   CHECK(end_char >= 0 && end_char <= static_cast<int>(line->size()));
     51   for (int i = begin_char; i < end_char; i++)
     52     line->at(i) = '-';
     53 }
     54 
     55 // The line length is used to clip the maximum length of the markers we'll
     56 // make if the error spans more than one line (like unterminated literals).
     57 void OutputHighlighedPosition(const Location& location,
     58                               const Err::RangeList& ranges,
     59                               size_t line_length) {
     60   // Make a buffer of the line in spaces.
     61   std::string highlight;
     62   highlight.resize(line_length);
     63   for (size_t i = 0; i < line_length; i++)
     64     highlight[i] = ' ';
     65 
     66   // Highlight all the ranges on the line.
     67   for (size_t i = 0; i < ranges.size(); i++)
     68     FillRangeOnLine(ranges[i], location.line_number(), &highlight);
     69 
     70   // Allow the marker to be one past the end of the line for marking the end.
     71   highlight.push_back(' ');
     72   CHECK(location.char_offset() - 1 >= 0 &&
     73         location.char_offset() - 1 < static_cast<int>(highlight.size()));
     74   highlight[location.char_offset() - 1] = '^';
     75 
     76   // Trim unused spaces from end of line.
     77   while (!highlight.empty() && highlight[highlight.size() - 1] == ' ')
     78     highlight.resize(highlight.size() - 1);
     79 
     80   highlight += "\n";
     81   OutputString(highlight, DECORATION_BLUE);
     82 }
     83 
     84 }  // namespace
     85 
     86 Err::Err() : has_error_(false) {
     87 }
     88 
     89 Err::Err(const Location& location,
     90          const std::string& msg,
     91          const std::string& help)
     92     : has_error_(true),
     93       location_(location),
     94       message_(msg),
     95       help_text_(help) {
     96 }
     97 
     98 Err::Err(const LocationRange& range,
     99          const std::string& msg,
    100          const std::string& help)
    101     : has_error_(true),
    102       location_(range.begin()),
    103       message_(msg),
    104       help_text_(help) {
    105   ranges_.push_back(range);
    106 }
    107 
    108 Err::Err(const Token& token,
    109          const std::string& msg,
    110          const std::string& help)
    111     : has_error_(true),
    112       location_(token.location()),
    113       message_(msg),
    114       help_text_(help) {
    115   ranges_.push_back(token.range());
    116 }
    117 
    118 Err::Err(const ParseNode* node,
    119          const std::string& msg,
    120          const std::string& help_text)
    121     : has_error_(true),
    122       message_(msg),
    123       help_text_(help_text) {
    124   // Node will be null in certain tests.
    125   if (node) {
    126     LocationRange range = node->GetRange();
    127     location_ = range.begin();
    128     ranges_.push_back(range);
    129   }
    130 }
    131 
    132 Err::Err(const Value& value,
    133          const std::string msg,
    134          const std::string& help_text)
    135     : has_error_(true),
    136       message_(msg),
    137       help_text_(help_text) {
    138   if (value.origin()) {
    139     LocationRange range = value.origin()->GetRange();
    140     location_ = range.begin();
    141     ranges_.push_back(range);
    142   }
    143 }
    144 
    145 Err::~Err() {
    146 }
    147 
    148 void Err::PrintToStdout() const {
    149   InternalPrintToStdout(false);
    150 }
    151 
    152 void Err::AppendSubErr(const Err& err) {
    153   sub_errs_.push_back(err);
    154 }
    155 
    156 void Err::InternalPrintToStdout(bool is_sub_err) const {
    157   DCHECK(has_error_);
    158 
    159   if (!is_sub_err)
    160     OutputString("ERROR ", DECORATION_RED);
    161 
    162   // File name and location.
    163   const InputFile* input_file = location_.file();
    164   std::string loc_str = location_.Describe(true);
    165   if (!loc_str.empty()) {
    166     if (is_sub_err)
    167       loc_str.insert(0, "See ");
    168     else
    169       loc_str.insert(0, "at ");
    170     loc_str.append(": ");
    171   }
    172   OutputString(loc_str + message_ + "\n");
    173 
    174   // Quoted line.
    175   if (input_file) {
    176     std::string line = GetNthLine(input_file->contents(),
    177                                   location_.line_number());
    178     if (!base::ContainsOnlyChars(line, base::kWhitespaceASCII)) {
    179       OutputString(line + "\n", DECORATION_DIM);
    180       OutputHighlighedPosition(location_, ranges_, line.size());
    181     }
    182   }
    183 
    184   // Optional help text.
    185   if (!help_text_.empty())
    186     OutputString(help_text_ + "\n");
    187 
    188   // Sub errors.
    189   for (size_t i = 0; i < sub_errs_.size(); i++)
    190     sub_errs_[i].InternalPrintToStdout(true);
    191 }
    192