| 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 |