blob: 6b61f90e759c8d70b14f7fc2ecca7b47b9177dcb [file] [log] [blame]
// Copyright 2020 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_DIAGNOSTIC_TYPES_H_
#define TOOLS_FIDL_FIDLC_INCLUDE_FIDL_DIAGNOSTIC_TYPES_H_
#include <cassert>
#include <set>
#include <sstream>
#include <string_view>
#include "source_span.h"
#include "token.h"
namespace fidl {
// Forward decls
namespace raw {
class Attribute;
class AttributeList;
} // namespace raw
namespace flat {
struct Constant;
struct IdentifierConstant;
struct LiteralConstant;
struct TypeConstructor;
struct Type;
class TypeTemplate;
class Name;
} // namespace flat
namespace diagnostics {
namespace internal {
constexpr std::string_view kFormatMarker = "{}";
std::string Display(const std::string& s);
std::string Display(std::string_view s);
std::string Display(const std::set<std::string>& s);
std::string Display(const SourceSpan& s);
std::string Display(const Token::KindAndSubkind& t);
std::string Display(const raw::Attribute& a);
std::string Display(const raw::AttributeList& a);
std::string Display(const std::vector<std::string_view>& library_name);
std::string Display(const flat::Constant* c);
std::string Display(const flat::TypeConstructor* tc);
std::string Display(const flat::Type* t);
std::string Display(const flat::TypeTemplate* t);
std::string Display(const flat::Name& n);
template <typename T, typename = decltype(std::to_string(std::declval<T>()))>
std::string Display(T val) {
return std::to_string(val);
}
inline std::string FormatErr(std::string_view msg) {
// This assert should never fail, because FormatErr is only called by
// ReportError -- and calls to ReportError fail at compile time if the # of
// args passed in != the number of args in the Error definition.
assert(msg.find(kFormatMarker) == std::string::npos &&
"number of format string parameters '{}' != number of supplied arguments");
return std::string(msg);
}
template <typename T, typename... Rest>
std::string FormatErr(std::string_view msg, T t, Rest... rest) {
size_t i = msg.find(kFormatMarker);
// This assert should never fail (see non-template FormatErr)
assert(i != std::string::npos &&
"number of format string parameters '{}' != number of supplied arguments");
// Split string at marker, insert formatted parameter
std::stringstream s;
s << msg.substr(0, i) << Display(t)
<< msg.substr(i + kFormatMarker.length(), msg.length() - i - kFormatMarker.length());
return FormatErr(s.str(), rest...);
}
constexpr size_t count_format_args(std::string_view s) {
size_t i = s.find(kFormatMarker, 0);
size_t total = 0;
while (i != std::string::npos && i < s.size()) {
total++;
i = s.find(kFormatMarker, i + kFormatMarker.size());
}
return total;
}
} // namespace internal
struct DiagnosticDef {
constexpr DiagnosticDef(std::string_view msg) : msg(msg) {}
std::string_view msg;
};
// The definition of an error. All instances of ErrorDef are in diagnostics.h.
// Template args define format parameters in the error message.
template <typename... Args>
struct ErrorDef : DiagnosticDef {
constexpr ErrorDef(std::string_view msg) : DiagnosticDef(msg) {
// This can't be a static assert because msg is not constexpr.
assert(sizeof...(Args) == internal::count_format_args(msg) &&
"number of format string parameters '{}' != number of template arguments");
}
};
// The definition of a warning. All instances of WarningDef are in
// diagnostics.h. Template args define format parameters in the warning message.
template <typename... Args>
struct WarningDef : DiagnosticDef {
constexpr WarningDef(std::string_view msg) : DiagnosticDef(msg) {
// This can't be a static assert because msg is not constexpr.
assert(sizeof...(Args) == internal::count_format_args(msg) &&
"number of format string parameters '{}' != number of template arguments");
}
};
// A tag that indicates whether a diagnostic is an error or warning. In the
// future this could be extended to include hints, suggestions, etc.
enum class DiagnosticKind {
kError,
kWarning,
};
// Represents a given instance of an error. Points to the error type it is an
// instance of. Holds a SourceSpan indicating where the error occurred and a
// formatted error message, built from the ErrorDef's message template and
// format parameters passed in to Error's constructor.
// Exists in order to allow deferral of error reporting and to be able to pass
// around errors.
struct Diagnostic {
Diagnostic(DiagnosticKind kind, const DiagnosticDef& err, const std::optional<SourceSpan>& span,
const std::string msg)
: kind(kind), err(err), span(span), msg(msg) {}
Diagnostic(DiagnosticKind kind, const DiagnosticDef& err, const Token& token,
const std::string msg)
: kind(kind), err(err), span(token.span()), msg(msg) {}
virtual ~Diagnostic() {}
DiagnosticKind kind;
const DiagnosticDef& err;
std::optional<SourceSpan> span;
std::string msg;
};
template <typename... Args>
struct Error : Diagnostic {
Error(const ErrorDef<Args...>& err, std::optional<SourceSpan> span, Args... args)
: Diagnostic(DiagnosticKind::kError, err, span, internal::FormatErr(err.msg, args...)) {}
};
template <typename... Args>
struct Warning : Diagnostic {
Warning(const WarningDef<Args...>& warn, std::optional<SourceSpan> span, Args... args)
: Diagnostic(DiagnosticKind::kWarning, warn, span, internal::FormatErr(warn.msg, args...)) {}
};
} // namespace diagnostics
} // namespace fidl
#endif // TOOLS_FIDL_FIDLC_INCLUDE_FIDL_DIAGNOSTIC_TYPES_H_