1d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
3d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch// found in the LICENSE file.
4d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
5d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "tools/gn/err.h"
6d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
7d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "base/strings/string_number_conversions.h"
8d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "base/strings/string_util.h"
9d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "tools/gn/filesystem_utils.h"
10d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "tools/gn/input_file.h"
11d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "tools/gn/parse_tree.h"
12d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "tools/gn/standard_out.h"
13d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "tools/gn/tokenizer.h"
14d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch#include "tools/gn/value.h"
15d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
16d3868032626d59662ff73b372b5d584c1d144c53Ben Murdochnamespace {
17d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
18d3868032626d59662ff73b372b5d584c1d144c53Ben Murdochstd::string GetNthLine(const base::StringPiece& data, int n) {
19d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  size_t line_off = Tokenizer::ByteOffsetOfNthLine(data, n);
20d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  size_t end = line_off + 1;
21d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  while (end < data.size() && !Tokenizer::IsNewline(data, end))
22d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    end++;
23d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  return data.substr(line_off, end - line_off).as_string();
24d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
25d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
26d3868032626d59662ff73b372b5d584c1d144c53Ben Murdochvoid FillRangeOnLine(const LocationRange& range, int line_number,
27d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch                     std::string* line) {
28d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // Only bother if the range's begin or end overlaps the line. If the entire
29d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // line is highlighted as a result of this range, it's not very helpful.
30d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  if (range.begin().line_number() != line_number &&
31d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      range.end().line_number() != line_number)
32d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    return;
33d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
34d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // Watch out, the char offsets in the location are 1-based, so we have to
35d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // subtract 1.
36d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  int begin_char;
37d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  if (range.begin().line_number() < line_number)
38d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    begin_char = 0;
39d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  else
40d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    begin_char = range.begin().char_offset() - 1;
41d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
42d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  int end_char;
43d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  if (range.end().line_number() > line_number)
44d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    end_char = line->size();  // Ending is non-inclusive.
45d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  else
46d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    end_char = range.end().char_offset() - 1;
47d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
48d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  CHECK(end_char >= begin_char);
49d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  CHECK(begin_char >= 0 && begin_char <= static_cast<int>(line->size()));
50d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  CHECK(end_char >= 0 && end_char <= static_cast<int>(line->size()));
51d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  for (int i = begin_char; i < end_char; i++)
52d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    line->at(i) = '-';
53d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
54d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
55d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch// The line length is used to clip the maximum length of the markers we'll
56d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch// make if the error spans more than one line (like unterminated literals).
57d3868032626d59662ff73b372b5d584c1d144c53Ben Murdochvoid OutputHighlighedPosition(const Location& location,
58d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch                              const Err::RangeList& ranges,
59d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch                              size_t line_length) {
60d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // Make a buffer of the line in spaces.
61d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  std::string highlight;
62d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  highlight.resize(line_length);
63d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  for (size_t i = 0; i < line_length; i++)
64d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    highlight[i] = ' ';
65d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
66d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // Highlight all the ranges on the line.
67d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  for (size_t i = 0; i < ranges.size(); i++)
68d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    FillRangeOnLine(ranges[i], location.line_number(), &highlight);
69d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
70d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // Allow the marker to be one past the end of the line for marking the end.
71d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  highlight.push_back(' ');
72d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  CHECK(location.char_offset() - 1 >= 0 &&
73d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch        location.char_offset() - 1 < static_cast<int>(highlight.size()));
74d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  highlight[location.char_offset() - 1] = '^';
75d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
76d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // Trim unused spaces from end of line.
77d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  while (!highlight.empty() && highlight[highlight.size() - 1] == ' ')
78d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    highlight.resize(highlight.size() - 1);
79d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
80d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  highlight += "\n";
81d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  OutputString(highlight, DECORATION_BLUE);
82d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
83d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
84d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}  // namespace
85d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
86d3868032626d59662ff73b372b5d584c1d144c53Ben MurdochErr::Err() : has_error_(false) {
87d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
88d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
89d3868032626d59662ff73b372b5d584c1d144c53Ben MurdochErr::Err(const Location& location,
90d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch         const std::string& msg,
91d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch         const std::string& help)
92d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    : has_error_(true),
93d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      location_(location),
94d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      message_(msg),
95d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      help_text_(help) {
96d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
97d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
98d3868032626d59662ff73b372b5d584c1d144c53Ben MurdochErr::Err(const LocationRange& range,
99d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch         const std::string& msg,
100d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch         const std::string& help)
101d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    : has_error_(true),
102d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      location_(range.begin()),
103d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      message_(msg),
104d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      help_text_(help) {
105d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  ranges_.push_back(range);
106d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
107d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
108d3868032626d59662ff73b372b5d584c1d144c53Ben MurdochErr::Err(const Token& token,
109d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch         const std::string& msg,
110d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch         const std::string& help)
111d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    : has_error_(true),
112d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      location_(token.location()),
113d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      message_(msg),
114d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      help_text_(help) {
115d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  ranges_.push_back(token.range());
116d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
117d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
118d3868032626d59662ff73b372b5d584c1d144c53Ben MurdochErr::Err(const ParseNode* node,
119d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch         const std::string& msg,
120d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch         const std::string& help_text)
121d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    : has_error_(true),
122d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      message_(msg),
123d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      help_text_(help_text) {
124d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // Node will be null in certain tests.
125d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  if (node) {
126d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    LocationRange range = node->GetRange();
127d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    location_ = range.begin();
128d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    ranges_.push_back(range);
129d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  }
130d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
131d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
132d3868032626d59662ff73b372b5d584c1d144c53Ben MurdochErr::Err(const Value& value,
133d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch         const std::string msg,
134d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch         const std::string& help_text)
135d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    : has_error_(true),
136d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      message_(msg),
137d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      help_text_(help_text) {
138d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  if (value.origin()) {
139d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    LocationRange range = value.origin()->GetRange();
140d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    location_ = range.begin();
141d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    ranges_.push_back(range);
142d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  }
143d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
144d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
145d3868032626d59662ff73b372b5d584c1d144c53Ben MurdochErr::~Err() {
146d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
147d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
148d3868032626d59662ff73b372b5d584c1d144c53Ben Murdochvoid Err::PrintToStdout() const {
149d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  InternalPrintToStdout(false);
150d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
151d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
152d3868032626d59662ff73b372b5d584c1d144c53Ben Murdochvoid Err::AppendSubErr(const Err& err) {
153d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  sub_errs_.push_back(err);
154d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
155d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
156d3868032626d59662ff73b372b5d584c1d144c53Ben Murdochvoid Err::InternalPrintToStdout(bool is_sub_err) const {
157d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  DCHECK(has_error_);
158d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
159d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  if (!is_sub_err)
160d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    OutputString("ERROR ", DECORATION_RED);
161d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
162d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // File name and location.
163d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  const InputFile* input_file = location_.file();
164d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  std::string loc_str;
165d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  if (input_file) {
166d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    std::string path8;
167d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    path8.assign(input_file->name().value());
168d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
169d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    if (is_sub_err)
170d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      loc_str = "See ";
171d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    else
172d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      loc_str = "at ";
173d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    loc_str += path8 + ": " +
174d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch        base::IntToString(location_.line_number()) + ":" +
175d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch        base::IntToString(location_.char_offset()) + ": ";
176d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  }
177d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  OutputString(loc_str + message_ + "\n");
178d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
179d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // Quoted line.
180d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  if (input_file) {
181d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    std::string line = GetNthLine(input_file->contents(),
182d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch                                  location_.line_number());
183d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    if (!ContainsOnlyWhitespaceASCII(line)) {
184d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      OutputString(line + "\n", DECORATION_BOLD);
185d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch      OutputHighlighedPosition(location_, ranges_, line.size());
186d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    }
187d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  }
188d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
189d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // Optional help text.
190d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  if (!help_text_.empty())
191d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    OutputString(help_text_ + "\n");
192d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch
193d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  // Sub errors.
194d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch  for (size_t i = 0; i < sub_errs_.size(); i++)
195d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch    sub_errs_[i].InternalPrintToStdout(true);
196d3868032626d59662ff73b372b5d584c1d144c53Ben Murdoch}
197