// Copyright (C) 2020-2024 Free Software Foundation, Inc. // This file is part of GCC. // GCC is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3, or (at your option) any later // version. // GCC is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING3. If not see // . // rust-diagnostics.h -- interface to diagnostic reporting -*- C++ -*- #ifndef RUST_DIAGNOSTICS_H #define RUST_DIAGNOSTICS_H #include "rust-linemap.h" #include "util/optional.h" // This macro is used to specify the position of format string & it's // arguments within the function's paramter list. // 'm' specifies the position of the format string parameter. // 'n' specifies the position of the first argument for the format string. #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) #define RUST_ATTRIBUTE_GCC_DIAG(m, n) \ __attribute__ ((__format__ (__gcc_tdiag__, m, n))) \ __attribute__ ((__nonnull__ (m))) #else #define RUST_ATTRIBUTE_GCC_DIAG(m, n) #endif // These declarations define the interface through which the frontend // reports errors and warnings. These functions accept printf-like // format specifiers (e.g. %d, %f, %s, etc), with the following additional // extensions: // // 1. 'q' qualifier may be applied to a specifier to add quoting, e.g. // %qd produces a quoted decimal output, %qs a quoted string output. // [This extension is supported only with single-character format // specifiers]. // // 2. %m specifier outputs value of "strerror(errno)" at time of call. // // 3. %< outputs an opening quote, %> a closing quote. // // All other format specifiers are as defined by 'sprintf'. The final resulting // message is then sent to the back end via rust_be_error_at/rust_be_warning_at. // simple location // https://gist.github.com/MahadMuhammad/8c9d5fc88ea18d8c520937a8071d4185 // We want E0005 to be mapped to the value `5` - this way, we can easily format // it in `make_description`. We also want to keep the value "5" only once when // defining the error code in rust-error-codes.def, so not have ERROR(E0005, 5) // as that is error prone. If we just use `0005` as the discriminant for the // `E0005` enum variant, then we are actually creating octal values (!) as `0` // is the C/C++ octal prefix. So this does not work for `E0009` for example, // since 0009 is not a valid octal literal. // We can circumvent this by doing a little bit of constant folding in the // discriminant expression. So for ERROR(E0009), this macro expands to the // following variant: // // E0009 = (10009 - 10000) // // which gets folded to the result of the substraction... 9. A valid decimal // literal which corresponds to E0009. #define ERROR(N) E##N = (1##N - 10000) enum class ErrorCode : unsigned int { #include "rust-error-codes.def" }; #undef ERROR // clang-format off extern void rust_internal_error_at (const location_t, const char *fmt, ...) RUST_ATTRIBUTE_GCC_DIAG (2, 3) RUST_ATTRIBUTE_NORETURN; extern void rust_error_at (const location_t, const char *fmt, ...) RUST_ATTRIBUTE_GCC_DIAG (2, 3); extern void rust_error_at (const location_t, const ErrorCode, const char *fmt, ...) RUST_ATTRIBUTE_GCC_DIAG (3, 4); extern void rust_warning_at (const location_t, int opt, const char *fmt, ...) RUST_ATTRIBUTE_GCC_DIAG (3, 4); extern void rust_fatal_error (const location_t, const char *fmt, ...) RUST_ATTRIBUTE_GCC_DIAG (2, 3) RUST_ATTRIBUTE_NORETURN; extern void rust_inform (const location_t, const char *fmt, ...) RUST_ATTRIBUTE_GCC_DIAG (2, 3); // rich locations extern void rust_error_at (const rich_location &, const char *fmt, ...) RUST_ATTRIBUTE_GCC_DIAG (2, 3); extern void rust_error_at (const rich_location &, const ErrorCode, const char *fmt, ...) RUST_ATTRIBUTE_GCC_DIAG (3, 4); extern void /* similiar to other frontends */ rust_error_at(rich_location *richloc,const char *fmt, ...) RUST_ATTRIBUTE_GCC_DIAG (2, 3); extern void /* similiar to other frontends */ rust_error_at(rich_location *richloc, const ErrorCode, const char *fmt, ...) RUST_ATTRIBUTE_GCC_DIAG (3, 4); // clang-format on // These interfaces provide a way for the front end to ask for // the open/close quote characters it should use when formatting // diagnostics (warnings, errors). extern const char * rust_open_quote (); extern const char * rust_close_quote (); // These interfaces are used by utilities above to pass warnings and // errors (once format specifiers have been expanded) to the back end, // and to determine quoting style. Avoid calling these routines directly; // instead use the equivalent routines above. The back end is required to // implement these routines. // clang-format off extern void rust_be_internal_error_at (const location_t, const std::string &errmsg) RUST_ATTRIBUTE_NORETURN; extern void rust_be_error_at (const location_t, const std::string &errmsg); extern void rust_be_error_at (const location_t, const ErrorCode, const std::string &errmsg); extern void rust_be_error_at (const rich_location &, const std::string &errmsg); extern void rust_be_error_at (const rich_location &, const ErrorCode, const std::string &errmsg); extern void /* similiar to other frontends */ rust_be_error_at (rich_location *richloc, const std::string &errmsg); extern void /* similiar to other frontends */ rust_be_error_at (rich_location *richloc, const ErrorCode, const std::string &errmsg); extern void rust_be_warning_at (const location_t, int opt, const std::string &warningmsg); extern void rust_be_fatal_error (const location_t, const std::string &errmsg) RUST_ATTRIBUTE_NORETURN; extern void rust_be_inform (const location_t, const std::string &infomsg); extern void rust_be_get_quotechars (const char **open_quote, const char **close_quote); extern bool rust_be_debug_p (void); // clang-format on namespace Rust { /* A structure used to represent an error. Useful for enabling * errors to be ignored, e.g. if backtracking. */ struct Error { enum class Kind { Hint, Err, FatalErr, }; Kind kind; location_t locus; rich_location *richlocus = nullptr; ErrorCode errorcode; std::string message; bool is_errorcode = false; // simple location Error (Kind kind, location_t locus, std::string message) : kind (kind), locus (locus), message (std::move (message)) { message.shrink_to_fit (); } // simple location + error code Error (Kind kind, location_t locus, ErrorCode code, std::string message) : kind (kind), locus (locus), errorcode (std::move (code)), message (std::move (message)) { is_errorcode = true; message.shrink_to_fit (); } // rich location Error (Kind kind, rich_location *richlocus, std::string message) : kind (kind), richlocus (richlocus), message (std::move (message)) { message.shrink_to_fit (); } // rich location + error code Error (Kind kind, rich_location *richlocus, ErrorCode code, std::string message) : kind (kind), richlocus (richlocus), errorcode (std::move (code)), message (std::move (message)) { is_errorcode = true; message.shrink_to_fit (); } // simple location Error (location_t locus, std::string message) { Error (Kind::Err, locus, std::move (message)); } // simple location + error code Error (location_t locus, ErrorCode code, std::string message) { Error (Kind::Err, locus, std::move (code), std::move (message)); } // rich location Error (rich_location *richlocus, std::string message) { Error (Kind::Err, richlocus, std::move (message)); } // rich location + error code Error (rich_location *richlocus, ErrorCode code, std::string message) { Error (Kind::Err, richlocus, std::move (code), std::move (message)); } static Error Hint (location_t locus, std::string message) { return Error (Kind::Hint, locus, std::move (message)); } static Error Fatal (location_t locus, std::string message) { return Error (Kind::FatalErr, locus, std::move (message)); } // TODO: the attribute part might be incorrect // simple location Error (location_t locus, const char *fmt, ...) /*RUST_ATTRIBUTE_GCC_DIAG (2, 3)*/ RUST_ATTRIBUTE_GCC_DIAG (3, 4); // simple location + error code Error (location_t locus, ErrorCode code, const char *fmt, ...) /*RUST_ATTRIBUTE_GCC_DIAG (3, 4)*/ RUST_ATTRIBUTE_GCC_DIAG (4, 5); // rich location Error (rich_location *richlocus, const char *fmt, ...) /*RUST_ATTRIBUTE_GCC_DIAG (2, 3)*/ RUST_ATTRIBUTE_GCC_DIAG (3, 4); // rich location + error code Error (rich_location *richlocus, ErrorCode code, const char *fmt, ...) /*RUST_ATTRIBUTE_GCC_DIAG (3, 4)*/ RUST_ATTRIBUTE_GCC_DIAG (4, 5); /** * printf-like overload of Error::Hint */ static Error Hint (location_t locus, const char *fmt, ...) RUST_ATTRIBUTE_GCC_DIAG (2, 3); /** * printf-like overload of Error::Fatal */ static Error Fatal (location_t locus, const char *fmt, ...) RUST_ATTRIBUTE_GCC_DIAG (2, 3); void emit () const { switch (kind) { case Kind::Hint: rust_inform (locus, "%s", message.c_str ()); break; case Kind::Err: if (is_errorcode) { if (richlocus == nullptr) rust_error_at (locus, errorcode, "%s", message.c_str ()); else rust_error_at (*richlocus, errorcode, "%s", message.c_str ()); } else { if (richlocus == nullptr) rust_error_at (locus, "%s", message.c_str ()); else rust_error_at (*richlocus, "%s", message.c_str ()); } break; case Kind::FatalErr: rust_fatal_error (locus, "%s", message.c_str ()); break; } } }; } // namespace Rust // rust_debug uses normal printf formatting, not GCC diagnostic formatting. #define rust_debug(...) rust_debug_loc (UNDEF_LOCATION, __VA_ARGS__) #define rust_sorry_at(location, ...) sorry_at (location, __VA_ARGS__) void rust_debug_loc (const location_t location, const char *fmt, ...) ATTRIBUTE_PRINTF_2; #endif // !defined(RUST_DIAGNOSTICS_H)