| // 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_ |