OLD | NEW |
(Empty) | |
| 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 = 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; |
| 165 if (input_file) { |
| 166 std::string path8; |
| 167 path8.assign(input_file->name().value()); |
| 168 |
| 169 if (is_sub_err) |
| 170 loc_str = "See "; |
| 171 else |
| 172 loc_str = "at "; |
| 173 loc_str += path8 + ": " + |
| 174 base::IntToString(location_.line_number()) + ":" + |
| 175 base::IntToString(location_.char_offset()) + ": "; |
| 176 } |
| 177 OutputString(loc_str + message_ + "\n"); |
| 178 |
| 179 // Quoted line. |
| 180 if (input_file) { |
| 181 std::string line = GetNthLine(input_file->contents(), |
| 182 location_.line_number()); |
| 183 if (!ContainsOnlyWhitespaceASCII(line)) { |
| 184 OutputString(line + "\n", DECORATION_BOLD); |
| 185 OutputHighlighedPosition(location_, ranges_, line.size()); |
| 186 } |
| 187 } |
| 188 |
| 189 // Optional help text. |
| 190 if (!help_text_.empty()) |
| 191 OutputString(help_text_ + "\n"); |
| 192 |
| 193 // Sub errors. |
| 194 for (size_t i = 0; i < sub_errs_.size(); i++) |
| 195 sub_errs_[i].InternalPrintToStdout(true); |
| 196 } |
OLD | NEW |