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