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.
5 #include "tools/gn/err.h"
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"
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
))
23 return data
.substr(line_off
, end
- line_off
).as_string();
26 void FillRangeOnLine(const LocationRange
& range
, int line_number
,
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
)
34 // Watch out, the char offsets in the location are 1-based, so we have to
37 if (range
.begin().line_number() < line_number
)
40 begin_char
= range
.begin().char_offset() - 1;
43 if (range
.end().line_number() > line_number
)
44 end_char
= static_cast<int>(line
->size()); // Ending is non-inclusive.
46 end_char
= range
.end().char_offset() - 1;
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
++)
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
,
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
++)
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
);
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] = '^';
76 // Trim unused spaces from end of line.
77 while (!highlight
.empty() && highlight
[highlight
.size() - 1] == ' ')
78 highlight
.resize(highlight
.size() - 1);
81 OutputString(highlight
, DECORATION_BLUE
);
86 Err::Err() : has_error_(false) {
89 Err::Err(const Location
& location
,
90 const std::string
& msg
,
91 const std::string
& help
)
98 Err::Err(const LocationRange
& range
,
99 const std::string
& msg
,
100 const std::string
& help
)
102 location_(range
.begin()),
105 ranges_
.push_back(range
);
108 Err::Err(const Token
& token
,
109 const std::string
& msg
,
110 const std::string
& help
)
112 location_(token
.location()),
115 ranges_
.push_back(token
.range());
118 Err::Err(const ParseNode
* node
,
119 const std::string
& msg
,
120 const std::string
& help_text
)
123 help_text_(help_text
) {
124 // Node will be null in certain tests.
126 LocationRange range
= node
->GetRange();
127 location_
= range
.begin();
128 ranges_
.push_back(range
);
132 Err::Err(const Value
& value
,
133 const std::string msg
,
134 const std::string
& help_text
)
137 help_text_(help_text
) {
138 if (value
.origin()) {
139 LocationRange range
= value
.origin()->GetRange();
140 location_
= range
.begin();
141 ranges_
.push_back(range
);
148 void Err::PrintToStdout() const {
149 InternalPrintToStdout(false);
152 void Err::AppendSubErr(const Err
& err
) {
153 sub_errs_
.push_back(err
);
156 void Err::InternalPrintToStdout(bool is_sub_err
) const {
160 OutputString("ERROR ", DECORATION_RED
);
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()) {
167 loc_str
.insert(0, "See ");
169 loc_str
.insert(0, "at ");
170 loc_str
.append(": ");
172 OutputString(loc_str
+ message_
+ "\n");
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());
184 // Optional help text.
185 if (!help_text_
.empty())
186 OutputString(help_text_
+ "\n");
189 for (const auto& sub_err
: sub_errs_
)
190 sub_err
.InternalPrintToStdout(true);