blob: cd4383a437bf5e36626042faae6f912466f75e24 [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 "fidl/flat_ast.h"
#include "fidl/names.h"
#include "fidl/raw_ast.h"
#include "fidl/source_span.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 std::optional<SourceSpan>& span,
std::string_view message, size_t squiggle_size) {
if (!span) {
std::string error = qualifier;
error.append(": ");
error.append(message);
return error;
}
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);
}
// Many editors and IDEs recognize errors in the form of
// filename:linenumber:column: error: descriptive-test-here\n
std::string error = span->position_str();
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;
}
namespace internal {
std::string Display(const std::string& s) { return s; }
// {'A', 'B', 'C'} -> "A, B, C"
std::string Display(const std::set<std::string>& s) {
std::stringstream ss;
for (auto it = s.begin(); it != s.end(); it++) {
if (it != s.cbegin()) {
ss << ", ";
}
ss << *it;
}
return ss.str();
}
std::string Display(const SourceSpan& s) { return s.position_str(); }
std::string Display(const raw::Attribute& a) { return a.name; }
std::string Display(raw::AttributeList* a) {
std::stringstream attributes_found;
for (auto it = a->attributes.begin(); it != a->attributes.end(); it++) {
if (it != a->attributes.cbegin()) {
attributes_found << ", ";
}
attributes_found << (*it).name;
}
return attributes_found.str();
}
std::string Display(const std::vector<std::string_view>& library_name) {
return NameLibrary(library_name);
}
std::string Display(const flat::Constant* c) { return NameFlatConstant(c); }
std::string Display(const flat::TypeConstructor* tc) { return NameFlatTypeConstructor(tc); }
std::string Display(const flat::Type* t) { return NameFlatType(t); }
std::string Display(const flat::TypeTemplate* t) { return NameFlatName(t->name()); }
std::string Display(const flat::Name& n) { return std::string(n.full_name()); }
} // namespace internal
void ErrorReporter::AddError(std::string formatted_message) {
if (mode_ == ReportingMode::kDoNotReport)
return;
errors_.push_back(std::move(formatted_message));
}
void ErrorReporter::AddWarning(std::string formatted_message) {
if (mode_ == ReportingMode::kDoNotReport)
return;
if (warnings_as_errors_) {
AddError(formatted_message);
} else {
warnings_.push_back(std::move(formatted_message));
}
}
// ReportError records an error with the span, message, source line, and
// position indicator.
//
// filename:line:col: error: message
// sourceline
// ^
void ErrorReporter::ReportError(const std::optional<SourceSpan>& span, std::string_view message) {
auto error = Format("error", span, message);
AddError(std::move(error));
}
// Records an error with the span, message, source line,
// position indicator, and tildes under the token reported.
//
// filename:line:col: error: message
// sourceline
// ^~~~
void ErrorReporter::ReportErrorWithSquiggle(const SourceSpan& span, std::string_view message) {
auto token_data = span.data();
auto error = Format("error", std::make_optional(span), message, token_data.size());
AddError(std::move(error));
}
// ReportError records an error with the span, message, source line,
// position indicator, and tildes under the token reported.
// Uses the given Token to get its span, and then calls
// ReportErrorWithSquiggle()
//
// filename:line:col: error: message
// sourceline
// ^~~~
void ErrorReporter::ReportError(const Token& token, std::string_view message) {
ReportErrorWithSquiggle(token.span(), 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 span, message, source line, and
// position indicator.
//
// filename:line:col: warning: message
// sourceline
// ^
void ErrorReporter::ReportWarning(const std::optional<SourceSpan>& span, std::string_view message) {
auto warning = Format("warning", span, message);
AddWarning(std::move(warning));
}
// Records a warning with the span, message, source line,
// position indicator, and tildes under the token reported.
//
// filename:line:col: warning: message
// sourceline
// ^~~~
void ErrorReporter::ReportWarningWithSquiggle(const SourceSpan& span, std::string_view message) {
auto token_data = span.data();
auto warning = Format("warning", std::make_optional(span), message, token_data.size());
AddWarning(std::move(warning));
}
void ErrorReporter::ReportWarning(const Token& token, std::string_view message) {
ReportWarningWithSquiggle(token.span(), message);
}
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