blob: 4e5c2ececd46b01ebd2a57879a376d115c98f320 [file] [log] [blame]
// rust-diagnostics.cc -- GCC implementation of rust diagnostics interface.
// Copyright (C) 2016-2023 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"
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 (Linemap::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;
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_internal_error_at (const Location 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_error_at (const 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_warning_at (const Location 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_fatal_error (const Location 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_inform (const Location 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_error_at (const RichLocation &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_debug_loc (const Location location, const char *fmt, ...)
{
if (!rust_be_debug_p ())
return;
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 (Linemap::unknown_location (),
"memory allocation failed in vasprintf");
rust_assert (0);
}
std::string rval = std::string (mbuf);
free (mbuf);
rust_be_inform (location, rval);
}
namespace Rust {
/**
* This function takes ownership of `args` and calls `va_end` on it
*/
static Error
va_constructor (Error::Kind kind, Location locus, const char *fmt, va_list args)
RUST_ATTRIBUTE_GCC_DIAG (3, 0);
static Error
va_constructor (Error::Kind kind, Location 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);
}
Error::Error (const Location location, const char *fmt, ...)
: kind (Kind::Err), locus (location)
{
va_list ap;
va_start (ap, fmt);
*this = va_constructor (Kind::Err, location, fmt, ap);
}
Error
Error::Hint (const Location location, const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
return va_constructor (Kind::Hint, location, fmt, ap);
}
Error
Error::Fatal (const Location location, const char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
return va_constructor (Kind::FatalErr, location, fmt, ap);
}
} // namespace Rust