blob: 04d26f576acf20e8f8bd487cb90f6f8689332736 [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 <zircon/assert.h>
#include <memory>
#include <set>
#include <sstream>
#include <string_view>
#include "flat_ast.h"
#include "source_span.h"
#include "token.h"
#include "types.h"
#include "utils.h"
#include "versioning_types.h"
namespace fidl {
// Forward decls
namespace raw {
class AttributeList;
} // namespace raw
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 std::set<std::string_view>& s);
std::string Display(const SourceSpan& s);
std::string Display(const Token::KindAndSubkind& t);
std::string Display(const types::Openness o);
std::string Display(const raw::AttributeList* a);
std::string Display(const std::vector<std::string_view>& library_name);
std::string Display(const flat::Attribute* a);
std::string Display(const flat::AttributeArg* a);
std::string Display(const flat::Constant* c);
std::string Display(flat::Element::Kind k);
std::string Display(flat::Decl::Kind k);
std::string Display(const flat::Element* e);
std::string Display(std::vector<const flat::Decl*>& d);
std::string Display(const flat::Type* t);
std::string Display(const flat::Name& n);
std::string Display(const Platform& p);
std::string Display(const Version& v);
std::string Display(const VersionRange& r);
template <typename T, typename = decltype(std::to_string(std::declval<T>()))>
std::string Display(T val) {
return std::to_string(val);
}
inline void FormatHelper(std::stringstream& out, std::string_view msg) {
ZX_ASSERT(msg.find(kFormatMarker) == std::string::npos);
out << msg;
}
template <typename T, typename... Rest>
void FormatHelper(std::stringstream& out, std::string_view msg, T t, const Rest&... rest) {
auto i = msg.find(kFormatMarker);
ZX_ASSERT(i != std::string::npos);
out << msg.substr(0, i) << Display(t);
auto remaining_msg = msg.substr(i + kFormatMarker.size());
FormatHelper(out, remaining_msg, rest...);
}
template <typename... Args>
std::string FormatDiagnostic(std::string_view msg, const Args&... args) {
std::stringstream s;
FormatHelper(s, msg, args...);
return s.str();
}
constexpr size_t CountFormatArgs(std::string_view s) {
size_t i = s.find(kFormatMarker, 0);
size_t total = 0;
while (i != std::string::npos) {
total++;
i = s.find(kFormatMarker, i + kFormatMarker.size());
}
return total;
}
// No-op non-constexpr function used to produce an error.
inline void IncorrectNumberOfFormatArgs() {}
template <typename... Args>
constexpr void CheckFormatArgs(std::string_view msg) {
static_assert(
(std::is_same_v<Args, std::remove_const_t<std::remove_reference_t<Args>>> && ...),
"remove redundant `const` or `&`; DiagnosticDef args are always passed by const reference");
static_assert(((!std::is_pointer_v<Args> || std::is_const_v<std::remove_pointer_t<Args>>)&&...),
"use a const pointer; DiagnosticDef args should not be mutable pointers");
static_assert(((!std::is_same_v<Args, std::string>)&&...),
"use std::string_view, not std::string");
// We can't static_assert below because the compiler doesn't know msg is
// always constexpr. Instead, we generate a "must be initialized by a constant
// expression" error by calling a non-constexpr function. The error only
// happens if the condition is true. Otherwise, the call gets eliminated.
if (sizeof...(Args) != internal::CountFormatArgs(msg)) {
IncorrectNumberOfFormatArgs();
}
}
} // namespace internal
using utils::identity_t;
struct DiagnosticDef {
constexpr explicit DiagnosticDef(std::string_view msg) : msg(msg) {}
DiagnosticDef(const DiagnosticDef&) = delete;
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 final : DiagnosticDef {
constexpr explicit ErrorDef(std::string_view msg) : DiagnosticDef(msg) {
internal::CheckFormatArgs<Args...>(msg);
}
};
// 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 final : DiagnosticDef {
constexpr explicit WarningDef(std::string_view msg) : DiagnosticDef(msg) {
internal::CheckFormatArgs<Args...>(msg);
}
};
// 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,
};
// A Diagnostic is the result of instantiating a DiagnosticDef with arguments.
// It stores a formatted std::string where "{}" markers have been replaced by
// arguments. It also stores a SourceSpan indicating where the problem occurred.
struct Diagnostic {
template <typename... Args>
Diagnostic(DiagnosticKind kind, const DiagnosticDef& def, SourceSpan span, const Args&... args)
: kind(kind), def(def), span(span), msg(internal::FormatDiagnostic(def.msg, args...)) {}
Diagnostic(const Diagnostic&) = delete;
// The factory functions below could be constructors, and std::make_unique
// would work fine. However, template error messages are better with static
// functions because it doesn't have to try every constructor.
template <typename... Args>
static std::unique_ptr<Diagnostic> MakeError(const ErrorDef<Args...>& def, SourceSpan span,
const identity_t<Args>&... args) {
return std::make_unique<Diagnostic>(DiagnosticKind::kError, def, span, args...);
}
template <typename... Args>
static std::unique_ptr<Diagnostic> MakeWarning(const WarningDef<Args...>& def, SourceSpan span,
const identity_t<Args>&... args) {
return std::make_unique<Diagnostic>(DiagnosticKind::kWarning, def, span, args...);
}
DiagnosticKind kind;
const DiagnosticDef& def;
SourceSpan span;
std::string msg;
};
} // namespace fidl
#endif // TOOLS_FIDL_FIDLC_INCLUDE_FIDL_DIAGNOSTIC_TYPES_H_