// 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); }