| // rust-diagnostics.cc -- GCC implementation of rust diagnostics interface. | |
| // Copyright (C) 2016-2026 Free Software Foundation, Inc. | |
| // Contributed by Than McIntosh, Google. | |
| // 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 | |
| // <http://www.gnu.org/licenses/>. | |
| #include "rust-system.h" | |
| #include "rust-diagnostics.h" | |
| #include "options.h" | |
| #include "diagnostics/metadata.h" | |
| static std::string | |
| mformat_value () | |
| { | |
| return std::string (xstrerror (errno)); | |
| } | |
| // Rewrite a format string to expand any extensions not | |
| // supported by sprintf(). See comments in rust-diagnostics.h | |
| // for list of supported format specifiers. | |
| static std::string | |
| expand_format (const char *fmt) | |
| { | |
| std::stringstream ss; | |
| for (const char *c = fmt; *c; ++c) | |
| { | |
| if (*c != '%') | |
| { | |
| ss << *c; | |
| continue; | |
| } | |
| c++; | |
| switch (*c) | |
| { | |
| case '\0': | |
| { | |
| // malformed format string | |
| rust_unreachable (); | |
| } | |
| case '%': | |
| { | |
| ss << "%"; | |
| break; | |
| } | |
| case 'm': | |
| { | |
| ss << mformat_value (); | |
| break; | |
| } | |
| case '<': | |
| { | |
| ss << rust_open_quote (); | |
| break; | |
| } | |
| case '>': | |
| { | |
| ss << rust_close_quote (); | |
| break; | |
| } | |
| case 'q': | |
| { | |
| ss << rust_open_quote (); | |
| c++; | |
| if (*c == 'm') | |
| { | |
| ss << mformat_value (); | |
| } | |
| else | |
| { | |
| ss << "%" << *c; | |
| } | |
| ss << rust_close_quote (); | |
| break; | |
| } | |
| default: | |
| { | |
| ss << "%" << *c; | |
| } | |
| } | |
| } | |
| return ss.str (); | |
| } | |
| // Expand message format specifiers, using a combination of | |
| // expand_format above to handle extensions (ex: %m, %q) and vasprintf() | |
| // to handle regular printf-style formatting. A pragma is being used here to | |
| // suppress this warning: | |
| // | |
| // warning: function ‘std::__cxx11::string expand_message(const char*, | |
| // __va_list_tag*)’ might be a candidate for ‘gnu_printf’ format attribute | |
| // [-Wsuggest-attribute=format] | |
| // | |
| // What appears to be happening here is that the checker is deciding that | |
| // because of the call to vasprintf() (which has attribute gnu_printf), the | |
| // calling function must need to have attribute gnu_printf as well, even | |
| // though there is already an attribute declaration for it. | |
| static std::string expand_message (const char *fmt, va_list ap) | |
| RUST_ATTRIBUTE_GCC_DIAG (1, 0); | |
| #pragma GCC diagnostic push | |
| #pragma GCC diagnostic ignored "-Wsuggest-attribute=format" | |
| static std::string | |
| expand_message (const char *fmt, va_list ap) | |
| { | |
| char *mbuf = 0; | |
| std::string expanded_fmt = expand_format (fmt); | |
| int nwr = vasprintf (&mbuf, expanded_fmt.c_str (), ap); | |
| if (nwr == -1) | |
| { | |
| // memory allocation failed | |
| rust_be_error_at (UNKNOWN_LOCATION, | |
| "memory allocation failed in vasprintf"); | |
| rust_assert (0); | |
| } | |
| std::string rval = std::string (mbuf); | |
| free (mbuf); | |
| return rval; | |
| } | |
| #pragma GCC diagnostic pop | |
| static const char *cached_open_quote = NULL; | |
| static const char *cached_close_quote = NULL; | |
| void | |
| rust_be_get_quotechars (const char **open_qu, const char **close_qu) | |
| { | |
| *open_qu = open_quote; | |
| *close_qu = close_quote; | |
| } | |
| const char * | |
| rust_open_quote () | |
| { | |
| if (cached_open_quote == NULL) | |
| rust_be_get_quotechars (&cached_open_quote, &cached_close_quote); | |
| return cached_open_quote; | |
| } | |
| const char * | |
| rust_close_quote () | |
| { | |
| if (cached_close_quote == NULL) | |
| rust_be_get_quotechars (&cached_open_quote, &cached_close_quote); | |
| return cached_close_quote; | |
| } | |
| void | |
| rust_be_internal_error_at (const location_t location, const std::string &errmsg) | |
| { | |
| std::string loc_str = Linemap::location_to_string (location); | |
| if (loc_str.empty ()) | |
| internal_error ("%s", errmsg.c_str ()); | |
| else | |
| internal_error ("at %s, %s", loc_str.c_str (), errmsg.c_str ()); | |
| } | |
| void | |
| rust_internal_error_at (const location_t location, const char *fmt, ...) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| rust_be_internal_error_at (location, expand_message (fmt, ap)); | |
| va_end (ap); | |
| } | |
| void | |
| rust_be_error_at (const location_t location, const std::string &errmsg) | |
| { | |
| error_at (location, "%s", errmsg.c_str ()); | |
| } | |
| void | |
| rust_error_at (const location_t location, const char *fmt, ...) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| rust_be_error_at (location, expand_message (fmt, ap)); | |
| va_end (ap); | |
| } | |
| class rust_error_code_rule : public diagnostics::metadata::rule | |
| { | |
| public: | |
| rust_error_code_rule (const ErrorCode code) : m_code (code) {} | |
| void format_error_code (char *buffer) const | |
| { | |
| // we can use the `u` format specifier because the `ErrorCode` enum class | |
| // "inherits" from `unsigned int` - add a static assertion to make sure | |
| // that's the case before we do the formatting | |
| static_assert ( | |
| std::is_same<std::underlying_type<ErrorCode>::type, unsigned int>::value, | |
| "invalid format specifier for ErrorCode's underlying type"); | |
| snprintf (buffer, 6, "E%04u", | |
| (std::underlying_type<ErrorCode>::type) m_code); | |
| } | |
| char *make_description () const final override | |
| { | |
| // 'E' + 4 characters + \0 | |
| char *buffer = static_cast<char *> (xcalloc (6, sizeof (char))); | |
| format_error_code (buffer); | |
| return buffer; | |
| } | |
| char *make_url () const final override | |
| { | |
| char buffer[6] = {0}; | |
| format_error_code (buffer); | |
| return concat ("https://doc.rust-lang.org/error-index.html#", buffer, NULL); | |
| } | |
| private: | |
| const ErrorCode m_code; | |
| }; | |
| void | |
| rust_be_error_at (const location_t location, const ErrorCode code, | |
| const std::string &errmsg) | |
| { | |
| rich_location gcc_loc (line_table, location); | |
| diagnostics::metadata m; | |
| rust_error_code_rule rule (code); | |
| m.add_rule (rule); | |
| error_meta (&gcc_loc, m, "%s", errmsg.c_str ()); | |
| } | |
| void | |
| rust_error_at (const location_t location, const ErrorCode code, const char *fmt, | |
| ...) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| rust_be_error_at (location, code, expand_message (fmt, ap)); | |
| va_end (ap); | |
| } | |
| void | |
| rust_be_error_at (const rich_location &location, const ErrorCode code, | |
| const std::string &errmsg) | |
| { | |
| /* TODO: 'error_at' would like a non-'const' 'rich_location *'. */ | |
| rich_location &gcc_loc = const_cast<rich_location &> (location); | |
| diagnostics::metadata m; | |
| rust_error_code_rule rule (code); | |
| m.add_rule (rule); | |
| error_meta (&gcc_loc, m, "%s", errmsg.c_str ()); | |
| } | |
| void | |
| rust_error_at (const rich_location &location, const ErrorCode code, | |
| const char *fmt, ...) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| rust_be_error_at (location, code, expand_message (fmt, ap)); | |
| va_end (ap); | |
| } | |
| void | |
| rust_be_error_at (rich_location *richloc, const ErrorCode code, | |
| const std::string &errmsg) | |
| { | |
| diagnostics::metadata m; | |
| rust_error_code_rule rule (code); | |
| m.add_rule (rule); | |
| error_meta (richloc, m, "%s", errmsg.c_str ()); | |
| } | |
| void | |
| rust_error_at (rich_location *richloc, const ErrorCode code, const char *fmt, | |
| ...) | |
| { | |
| /* TODO: Refactoring diagnostics to this overload */ | |
| va_list ap; | |
| va_start (ap, fmt); | |
| rust_be_error_at (richloc, code, expand_message (fmt, ap)); | |
| va_end (ap); | |
| } | |
| void | |
| rust_be_warning_at (const location_t location, int opt, | |
| const std::string &warningmsg) | |
| { | |
| warning_at (location, opt, "%s", warningmsg.c_str ()); | |
| } | |
| void | |
| rust_warning_at (const location_t location, int opt, const char *fmt, ...) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| rust_be_warning_at (location, opt, expand_message (fmt, ap)); | |
| va_end (ap); | |
| } | |
| void | |
| rust_be_fatal_error (const location_t location, const std::string &fatalmsg) | |
| { | |
| fatal_error (location, "%s", fatalmsg.c_str ()); | |
| } | |
| void | |
| rust_fatal_error (const location_t location, const char *fmt, ...) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| rust_be_fatal_error (location, expand_message (fmt, ap)); | |
| va_end (ap); | |
| } | |
| void | |
| rust_be_inform (const location_t location, const std::string &infomsg) | |
| { | |
| inform (location, "%s", infomsg.c_str ()); | |
| } | |
| void | |
| rust_inform (const location_t location, const char *fmt, ...) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| rust_be_inform (location, expand_message (fmt, ap)); | |
| va_end (ap); | |
| } | |
| // Rich Locations | |
| void | |
| rust_be_error_at (const rich_location &location, const std::string &errmsg) | |
| { | |
| /* TODO: 'error_at' would like a non-'const' 'rich_location *'. */ | |
| rich_location &gcc_loc = const_cast<rich_location &> (location); | |
| error_at (&gcc_loc, "%s", errmsg.c_str ()); | |
| } | |
| void | |
| rust_error_at (const rich_location &location, const char *fmt, ...) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| rust_be_error_at (location, expand_message (fmt, ap)); | |
| va_end (ap); | |
| } | |
| void | |
| rust_be_error_at (rich_location *richloc, const std::string &errmsg) | |
| { | |
| error_at (richloc, "%s", errmsg.c_str ()); | |
| } | |
| void | |
| rust_error_at (rich_location *richloc, const char *fmt, ...) | |
| { | |
| /* TODO: Refactoring diagnostics to this overload */ | |
| va_list ap; | |
| va_start (ap, fmt); | |
| rust_be_error_at (richloc, expand_message (fmt, ap)); | |
| va_end (ap); | |
| } | |
| bool | |
| rust_be_debug_p (void) | |
| { | |
| return !!flag_rust_debug; | |
| } | |
| void | |
| rust_debug_loc_internal (const location_t location, const char *fmt, ...) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| char *mbuf = NULL; | |
| int nwr = vasprintf (&mbuf, fmt, ap); | |
| va_end (ap); | |
| if (nwr == -1) | |
| { | |
| rust_be_error_at (UNKNOWN_LOCATION, | |
| "memory allocation failed in vasprintf"); | |
| rust_assert (0); | |
| } | |
| std::string rval = std::string (mbuf); | |
| free (mbuf); | |
| rust_be_inform (location, rval); | |
| } | |
| void | |
| rust_debug_fmt_at (const location_t location, const char *fmt, ...) | |
| { | |
| if (!rust_be_debug_p ()) | |
| return; | |
| va_list ap; | |
| va_start (ap, fmt); | |
| rust_be_inform (location, expand_message (fmt, ap)); | |
| va_end (ap); | |
| } | |
| namespace Rust { | |
| /** | |
| * This function takes ownership of `args` and calls `va_end` on it | |
| */ | |
| // simple location | |
| static Error va_constructor (Error::Kind kind, location_t locus, | |
| const char *fmt, va_list args) | |
| RUST_ATTRIBUTE_GCC_DIAG (3, 0); | |
| // simple location + error code | |
| static Error va_constructor (Error::Kind kind, location_t locus, | |
| const ErrorCode code, const char *fmt, | |
| va_list args) RUST_ATTRIBUTE_GCC_DIAG (4, 0); | |
| // rich location | |
| static Error va_constructor (Error::Kind kind, rich_location *r_locus, | |
| const char *fmt, va_list args) | |
| RUST_ATTRIBUTE_GCC_DIAG (3, 0); | |
| // rich location + error code | |
| static Error va_constructor (Error::Kind kind, rich_location *r_locus, | |
| const ErrorCode code, const char *fmt, | |
| va_list args) RUST_ATTRIBUTE_GCC_DIAG (4, 0); | |
| // simple location | |
| static Error | |
| va_constructor (Error::Kind kind, location_t locus, const char *fmt, | |
| va_list args) | |
| { | |
| std::string message = expand_message (fmt, args); | |
| message.shrink_to_fit (); | |
| va_end (args); | |
| return Error (kind, locus, message); | |
| } | |
| // simple location + error code | |
| static Error | |
| va_constructor (Error::Kind kind, location_t locus, const ErrorCode code, | |
| const char *fmt, va_list args) | |
| { | |
| std::string message = expand_message (fmt, args); | |
| message.shrink_to_fit (); | |
| va_end (args); | |
| return Error (kind, locus, code, message); | |
| } | |
| // rich location | |
| static Error | |
| va_constructor (Error::Kind kind, rich_location *r_locus, const char *fmt, | |
| va_list args) | |
| { | |
| std::string message = expand_message (fmt, args); | |
| message.shrink_to_fit (); | |
| va_end (args); | |
| return Error (kind, r_locus, message); | |
| } | |
| // rich location + error code | |
| static Error | |
| va_constructor (Error::Kind kind, rich_location *r_locus, const ErrorCode code, | |
| const char *fmt, va_list args) | |
| { | |
| std::string message = expand_message (fmt, args); | |
| message.shrink_to_fit (); | |
| va_end (args); | |
| return Error (kind, r_locus, code, message); | |
| } | |
| // simple location | |
| Error::Error (const location_t location, const char *fmt, ...) | |
| : kind (Kind::Err), locus (location) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| *this = va_constructor (Kind::Err, location, fmt, ap); | |
| } | |
| // simple location + error code | |
| Error::Error (const location_t location, const ErrorCode code, const char *fmt, | |
| ...) | |
| : kind (Kind::Err), locus (location), errorcode (code) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| *this = va_constructor (Kind::Err, location, code, fmt, ap); | |
| } | |
| // rich location | |
| Error::Error (rich_location *r_locus, const char *fmt, ...) | |
| : kind (Kind::Err), richlocus (r_locus) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| *this = va_constructor (Kind::Err, r_locus, fmt, ap); | |
| } | |
| // rich location + error code | |
| Error::Error (rich_location *r_locus, const ErrorCode code, const char *fmt, | |
| ...) | |
| : kind (Kind::Err), richlocus (r_locus), errorcode (code) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| *this = va_constructor (Kind::Err, r_locus, code, fmt, ap); | |
| } | |
| Error | |
| Error::Hint (const location_t location, const char *fmt, ...) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| return va_constructor (Kind::Hint, location, fmt, ap); | |
| } | |
| Error | |
| Error::Fatal (const location_t location, const char *fmt, ...) | |
| { | |
| va_list ap; | |
| va_start (ap, fmt); | |
| return va_constructor (Kind::FatalErr, location, fmt, ap); | |
| } | |
| } // namespace Rust |