| // go-diagnostics.cc -- Go error/warning diagnostics utilities. |
| |
| // Copyright 2016 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| #include "go-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 go-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 |
| go_unreachable(); |
| } |
| case '%': |
| { |
| ss << "%"; |
| break; |
| } |
| case 'm': |
| { |
| ss << mformat_value(); |
| break; |
| } |
| case '<': |
| { |
| ss << go_open_quote(); |
| break; |
| } |
| case '>': |
| { |
| ss << go_close_quote(); |
| break; |
| } |
| case 'q': |
| { |
| ss << go_open_quote(); |
| c++; |
| if (*c == 'm') |
| { |
| ss << mformat_value(); |
| } |
| else |
| { |
| ss << "%" << *c; |
| } |
| ss << go_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) GO_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 |
| go_be_error_at(Linemap::unknown_location(), |
| "memory allocation failed in vasprintf"); |
| go_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* |
| go_open_quote() |
| { |
| if (cached_open_quote == NULL) |
| go_be_get_quotechars(&cached_open_quote, &cached_close_quote); |
| return cached_open_quote; |
| } |
| |
| const char* |
| go_close_quote() |
| { |
| if (cached_close_quote == NULL) |
| go_be_get_quotechars(&cached_open_quote, &cached_close_quote); |
| return cached_close_quote; |
| } |
| |
| void |
| go_error_at(const Location location, const char* fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| go_be_error_at(location, expand_message(fmt, ap)); |
| va_end(ap); |
| } |
| |
| void |
| go_warning_at(const Location location, int opt, const char* fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| go_be_warning_at(location, opt, expand_message(fmt, ap)); |
| va_end(ap); |
| } |
| |
| void |
| go_fatal_error(const Location location, const char* fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| go_be_fatal_error(location, expand_message(fmt, ap)); |
| va_end(ap); |
| } |
| |
| void |
| go_inform(const Location location, const char* fmt, ...) |
| { |
| va_list ap; |
| |
| va_start(ap, fmt); |
| go_be_inform(location, expand_message(fmt, ap)); |
| va_end(ap); |
| } |
| |
| // go_debug uses normal printf formatting, not GCC diagnostic formatting. |
| |
| void |
| go_debug(const Location 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) |
| { |
| go_be_error_at(Linemap::unknown_location(), |
| "memory allocation failed in vasprintf"); |
| go_assert(0); |
| } |
| std::string rval = std::string(mbuf); |
| free(mbuf); |
| go_be_inform(location, rval); |
| } |