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 |