blob: 3224d33f865866277c5f100baad926c67c718633 [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 "fidl/source_location.h"
#include "fidl/token.h"
namespace fidl {
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 SourceLocation* maybe_location,
std::string_view message, size_t squiggle_size = 0u) {
if (!maybe_location) {
std::string error = qualifier;
error.append(": ");
error.append(message);
return error;
}
const auto& location = *maybe_location;
SourceFile::Position position;
std::string surrounding_line = std::string(location.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);
}
// Many editors and IDEs recognize errors in the form of
// filename:linenumber:column: error: descriptive-test-here\n
std::string error = location.position();
error.append(": ");
error.append(qualifier);
error.append(": ");
error.append(message);
error.push_back('\n');
error.append(surrounding_line);
error.push_back('\n');
error.append(squiggle);
return error;
}
void ErrorReporter::AddError(std::string formatted_message) {
errors_.push_back(std::move(formatted_message));
}
void ErrorReporter::AddWarning(std::string formatted_message) {
if (warnings_as_errors_) {
AddError(formatted_message);
} else {
warnings_.push_back(std::move(formatted_message));
}
}
// ReportError records an error with the location, message, source line, and
// position indicator.
//
// filename:line:col: error: message
// sourceline
// ^
void ErrorReporter::ReportError(const SourceLocation* maybe_location, std::string_view message) {
auto error = Format("error", maybe_location, message);
AddError(std::move(error));
}
// Records an error with the location, message, source line,
// position indicator, and tildes under the token reported.
//
// filename:line:col: error: message
// sourceline
// ^~~~
void ErrorReporter::ReportErrorWithSquiggle(
const SourceLocation& location, std::string_view message) {
auto token_data = location.data();
auto error = Format("error", &location, message, token_data.size());
AddError(std::move(error));
}
// ReportError records an error with the location, message, source line,
// position indicator, and tildes under the token reported.
// Uses the given Token to get its location, and then calls
// ReportErrortLocationWithSquiggle()
//
// filename:line:col: error: message
// sourceline
// ^~~~
void ErrorReporter::ReportError(const Token& token, std::string_view message) {
ReportErrorWithSquiggle(token.location(), message);
}
// ReportError records the provided message.
void ErrorReporter::ReportError(std::string_view message) {
std::string error("error: ");
error.append(message);
AddError(std::move(error));
}
// ReportWarning records a warning with the location, message, source line, and
// position indicator.
//
// filename:line:col: warning: message
// sourceline
// ^
void ErrorReporter::ReportWarning(const SourceLocation* maybe_location, std::string_view message) {
auto warning = Format("warning", maybe_location, message);
AddWarning(std::move(warning));
}
// Records a warning with the location, message, source line,
// position indicator, and tildes under the token reported.
//
// filename:line:col: warning: message
// sourceline
// ^~~~
void ErrorReporter::ReportWarningWithSquiggle(
const SourceLocation& location, std::string_view message) {
auto token_data = location.data();
auto warning = Format("warning", &location, message, token_data.size());
AddWarning(std::move(warning));
}
void ErrorReporter::PrintReports() {
for (const auto& error : errors_) {
fprintf(stderr, "%s\n", error.data());
}
for (const auto& warning : warnings_) {
fprintf(stderr, "%s\n", warning.data());
}
}
} // namespace fidl