• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetNthLine(const base::StringPiece & data,int n)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 
FillRangeOnLine(const LocationRange & range,int line_number,std::string * line)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).
OutputHighlighedPosition(const Location & location,const Err::RangeList & ranges,size_t line_length)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 
Err()86 Err::Err() : has_error_(false) {
87 }
88 
Err(const Location & location,const std::string & msg,const std::string & help)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 
Err(const LocationRange & range,const std::string & msg,const std::string & help)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 
Err(const Token & token,const std::string & msg,const std::string & help)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 
Err(const ParseNode * node,const std::string & msg,const std::string & help_text)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 
Err(const Value & value,const std::string msg,const std::string & help_text)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 
~Err()145 Err::~Err() {
146 }
147 
PrintToStdout() const148 void Err::PrintToStdout() const {
149   InternalPrintToStdout(false);
150 }
151 
AppendSubErr(const Err & err)152 void Err::AppendSubErr(const Err& err) {
153   sub_errs_.push_back(err);
154 }
155 
InternalPrintToStdout(bool is_sub_err) const156 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