blob: 9a8732d4a19d872e4f31334c555d3509d09e1e35 [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.
#ifndef TOOLS_FIDL_FIDLC_INCLUDE_FIDL_REPORTER_H_
#define TOOLS_FIDL_FIDLC_INCLUDE_FIDL_REPORTER_H_
#include <algorithm>
#include <cassert>
#include <optional>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
#include "diagnostics.h"
#include "source_span.h"
#include "token.h"
namespace fidl {
namespace reporter {
using diagnostics::Diagnostic;
using diagnostics::DiagnosticKind;
using diagnostics::Error;
using diagnostics::ErrorDef;
using diagnostics::Warning;
using diagnostics::WarningDef;
std::string MakeSquiggle(const std::string& surrounding_line, int column);
std::string Format(std::string qualifier, const std::optional<SourceSpan>& span,
std::string_view message, bool color, size_t squiggle_size = 0u);
class Reporter {
public:
Reporter(bool warnings_as_errors = false, bool enable_color = false)
: warnings_as_errors_(warnings_as_errors), enable_color_(enable_color) {}
// Enables temporarily muting reporting.
enum class ReportingMode {
kReport,
kDoNotReport,
};
// Controls a scoped override of the reporting mode of the error reporter.
// Resets the mode to its previous value on destruction.
class ScopedReportingMode {
public:
~ScopedReportingMode() { source_ = prev_value_; }
private:
friend class Reporter;
ScopedReportingMode(ReportingMode& source, ReportingMode value)
: prev_value_(source), source_(source) {
source_ = value;
}
ReportingMode prev_value_;
ReportingMode& source_;
};
class Counts {
public:
Counts(const Reporter* reporter)
: reporter_(reporter),
num_errors_(reporter->errors().size()),
num_warnings_(reporter->warnings().size()) {}
bool NoNewErrors() { return num_errors_ == reporter_->errors().size(); }
bool NoNewWarning() { return num_warnings_ == reporter_->warnings().size(); }
private:
const Reporter* reporter_;
const size_t num_errors_;
const size_t num_warnings_;
};
// Used to create a std::unique_ptr<Error> or std::unique_ptr<Warning> rather than
// std::make_unique to avoid having to specify the Args... template parameters on Error
// explicitly.
template <typename... Args>
static std::unique_ptr<Error<Args...>> MakeError(const ErrorDef<Args...>& err,
const std::optional<SourceSpan>& span,
Args... args) {
return std::make_unique<Error<Args...>>(err, span, args...);
}
template <typename... Args>
static std::unique_ptr<Error<Args...>> MakeError(const ErrorDef<Args...>& err, Args... args) {
return std::make_unique<Error<Args...>>(err, std::nullopt, args...);
}
template <typename... Args>
static std::unique_ptr<Warning<Args...>> MakeWarning(const WarningDef<Args...>& warn,
const std::optional<SourceSpan>& span,
Args... args) {
return std::make_unique<Warning<Args...>>(warn, span, args...);
}
template <typename... Args>
static std::unique_ptr<Warning<Args...>> MakeWarning(const WarningDef<Args...>& warn,
Args... args) {
return std::make_unique<Warning<Args...>>(warn, std::nullopt, args...);
}
template <typename... Args>
void Report(const ErrorDef<Args...>& err, const Args&... args) {
Report(std::move(MakeError(err, args...)));
}
template <typename... Args>
void Report(const ErrorDef<Args...>& err, const std::optional<SourceSpan>& span,
const Args&... args) {
Report(std::move(MakeError(err, span, args...)));
}
template <typename... Args>
void Report(const ErrorDef<Args...>& err, const Token& token, const Args&... args) {
Report(std::move(MakeError(err, token.span(), args...)));
}
template <typename... Args>
void Report(const WarningDef<Args...>& err, const Args&... args) {
Report(std::move(MakeWarning(err, args...)));
}
template <typename... Args>
void Report(const WarningDef<Args...>& warn, const std::optional<SourceSpan>& span,
const Args&... args) {
Report(std::move(MakeWarning(warn, span, args...)));
}
template <typename... Args>
void Report(const WarningDef<Args...>& err, const Token& token, const Args&... args) {
Report(std::move(MakeWarning(err, token.span(), args...)));
}
void Report(std::unique_ptr<Diagnostic> diag);
void PrintReports();
void PrintReportsJson();
Counts Checkpoint() const { return Counts(this); }
ScopedReportingMode OverrideMode(ReportingMode mode_override) {
return ScopedReportingMode(mode_, mode_override);
}
// Used to print reports. Combines errors and warnings and sorts by (file, span).
std::vector<Diagnostic*> diagnostics() const {
std::vector<Diagnostic*> diagnostics;
for (const auto& err : errors_) {
diagnostics.push_back(err.get());
}
for (const auto& warn : warnings_) {
diagnostics.push_back(warn.get());
}
// Sort by file > position > kind (errors then warnings) > message.
sort(diagnostics.begin(), diagnostics.end(), [](Diagnostic* a, Diagnostic* b) -> bool {
if (a->span && b->span) {
// SourceSpan overloads the < operator to compare by filename, then
// start position, then end position.
if (a->span < b->span)
return true;
if (b->span < a->span)
return false;
} else {
// Sort errors without spans first.
if (b->span)
return true;
if (a->span)
return false;
}
// If neither diagnostic had a span, or if their spans were ==, sort
// by kind (errors first) and then message.
if (a->kind == DiagnosticKind::kError && b->kind == DiagnosticKind::kWarning)
return true;
if (a->kind == DiagnosticKind::kWarning && b->kind == DiagnosticKind::kError)
return false;
return a->msg < b->msg;
});
return diagnostics;
}
const std::vector<std::unique_ptr<Diagnostic>>& errors() const { return errors_; }
const std::vector<std::unique_ptr<Diagnostic>>& warnings() const { return warnings_; }
void set_warnings_as_errors(bool value) { warnings_as_errors_ = value; }
private:
void AddError(std::unique_ptr<Diagnostic> err);
void AddWarning(std::unique_ptr<Diagnostic> warn);
ReportingMode mode_ = ReportingMode::kReport;
bool warnings_as_errors_;
bool enable_color_;
// The reporter collects error and warnings separately so that we can easily
// keep track of the current number of errors during compilation. The number
// of errors is used to determine whether the parser is in an `Ok` state.
std::vector<std::unique_ptr<Diagnostic>> errors_;
std::vector<std::unique_ptr<Diagnostic>> warnings_;
};
} // namespace reporter
} // namespace fidl
#endif // TOOLS_FIDL_FIDLC_INCLUDE_FIDL_REPORTER_H_