blob: d8f8c9f40e158a28e21ef0e795de147f63c88414 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "fidl/error_reporter.h"
#include <cassert>
#include <sstream>
#include "fidl/token.h"
namespace fidl {
namespace error_reporter {
std::string MakeSquiggle(const std::string& surrounding_line, int column) {
std::string squiggle;
size_t line_size = surrounding_line.size();
for (size_t i = 0; i < (static_cast<size_t>(column) - 1); i++) {
if (i < line_size && surrounding_line[i] == '\t') {
squiggle.push_back('\t');
} else {
squiggle.push_back(' ');
}
}
squiggle.push_back('^');
return squiggle;
}
std::string Format(std::string qualifier, const std::optional<SourceSpan>& span,
std::string_view message, bool color, size_t squiggle_size) {
const std::string_view bold = color ? "\033[1m" : "";
const std::string_view bold_red = color ? "\033[1;31m" : "";
const std::string_view bold_green = color ? "\033[1;32m" : "";
const std::string_view reset = color ? "\033[0m" : "";
if (!span) {
std::stringstream error;
error << bold_red << qualifier << ": " << reset;
error << bold << message << reset;
return error.str();
}
SourceFile::Position position;
std::string surrounding_line = std::string(span->SourceLine(&position));
assert(surrounding_line.find('\n') == std::string::npos &&
"A single line should not contain a newline character");
std::string squiggle = MakeSquiggle(surrounding_line, position.column);
if (squiggle_size != 0u) {
--squiggle_size;
}
squiggle += std::string(squiggle_size, '~');
// Some tokens (like string literals) can span multiple lines. Truncate the
// string to just one line at most.
//
// The +1 allows for squiggles at the end of line, which is useful when
// referencing the bounds of a file or line (e.g. unexpected end of file,
// expected something on an empty line).
size_t line_size = surrounding_line.size() + 1;
if (squiggle.size() > line_size) {
squiggle.resize(line_size);
}
std::stringstream error;
// Many editors and IDEs recognize errors in the form of
// filename:linenumber:column: error: descriptive-test-here\n
error << bold << span->position_str() << ": " << reset;
error << bold_red << qualifier << ": " << reset;
error << bold << message << reset;
error << '\n' << surrounding_line << '\n';
error << bold_green << squiggle << reset;
return error.str();
}
void ErrorReporter::AddError(std::unique_ptr<BaseError> err) {
if (mode_ == ReportingMode::kDoNotReport)
return;
errors_.push_back(std::move(err));
}
void ErrorReporter::AddWarning(std::unique_ptr<BaseError> warn) {
if (mode_ == ReportingMode::kDoNotReport)
return;
if (warnings_as_errors_) {
errors_.push_back(std::move(warn));
} else {
warnings_.push_back(std::move(warn));
}
}
// Record an error with the span, message, source line, position indicator,
// and, if span is not nullopt, tildes under the token reported.
//
// filename:line:col: {error, warning}: message
// sourceline
// ^~~~
void ErrorReporter::ReportError(std::unique_ptr<BaseError> err) {
assert(err && "should not report nullptr error");
AddError(std::move(err));
}
void ErrorReporter::ReportWarning(std::unique_ptr<BaseError> warn) {
assert(warn && "should not report nullptr warning");
AddWarning(std::move(warn));
}
void ErrorReporter::PrintReports() {
for (const auto& error : errors_) {
size_t squiggle_size = error->span ? error->span.value().data().size() : 0;
auto error_str = Format("error", error->span, error->msg, enable_color_, squiggle_size);
fprintf(stderr, "%s\n", error_str.c_str());
}
for (const auto& warning : warnings_) {
size_t squiggle_size = warning->span ? warning->span.value().data().size() : 0;
auto warning_str = Format("warning", warning->span, warning->msg,
enable_color_, squiggle_size);
fprintf(stderr, "%s\n", warning_str.c_str());
}
if (!errors_.empty() && warnings_.empty()) {
fprintf(stderr, "%zu error(s) reported.\n", errors_.size());
} else if (errors_.empty() && !warnings_.empty()) {
fprintf(stderr, "%zu warning(s) reported.\n", warnings_.size());
} else if (!errors_.empty() && !warnings_.empty()) {
fprintf(stderr, "%zu error(s) and %zu warning(s) reported.\n",
errors_.size(), warnings_.size());
}
}
} // namespace error_reporter
} // namespace fidl