1 // go-diagnostics.cc -- Go error/warning diagnostics utilities.
2 
3 // Copyright 2016 The Go Authors. All rights reserved.
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file.
6 
7 #include "go-diagnostics.h"
8 
9 static std::string
mformat_value()10 mformat_value()
11 {
12   return std::string(xstrerror(errno));
13 }
14 
15 // Rewrite a format string to expand any extensions not
16 // supported by sprintf(). See comments in go-diagnostics.h
17 // for list of supported format specifiers.
18 
19 static std::string
expand_format(const char * fmt)20 expand_format(const char* fmt)
21 {
22   std::stringstream ss;
23   for (const char* c = fmt; *c; ++c)
24     {
25       if (*c != '%')
26         {
27           ss << *c;
28           continue;
29         }
30       c++;
31       switch (*c)
32         {
33           case '\0':
34             {
35               // malformed format string
36               go_unreachable();
37             }
38           case '%':
39             {
40               ss << "%";
41               break;
42             }
43           case 'm':
44             {
45               ss << mformat_value();
46               break;
47             }
48           case '<':
49             {
50               ss << go_open_quote();
51               break;
52             }
53           case '>':
54             {
55               ss << go_close_quote();
56               break;
57             }
58           case 'q':
59             {
60               ss << go_open_quote();
61               c++;
62               if (*c == 'm')
63                 {
64                   ss << mformat_value();
65                 }
66               else
67                 {
68                   ss << "%" << *c;
69                 }
70               ss << go_close_quote();
71               break;
72             }
73           default:
74             {
75               ss << "%" << *c;
76             }
77         }
78     }
79   return ss.str();
80 }
81 
82 // Expand message format specifiers, using a combination of
83 // expand_format above to handle extensions (ex: %m, %q) and vasprintf()
84 // to handle regular printf-style formatting. A pragma is being used here to
85 // suppress this warning:
86 //
87 //   warning: function ‘std::__cxx11::string expand_message(const char*, __va_list_tag*)’ might be a candidate for ‘gnu_printf’ format attribute [-Wsuggest-attribute=format]
88 //
89 // What appears to be happening here is that the checker is deciding that
90 // because of the call to vasprintf() (which has attribute gnu_printf), the
91 // calling function must need to have attribute gnu_printf as well, even
92 // though there is already an attribute declaration for it.
93 
94 static std::string
95 expand_message(const char* fmt, va_list ap) GO_ATTRIBUTE_GCC_DIAG(1,0);
96 
97 #pragma GCC diagnostic push
98 #pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
99 
100 static std::string
expand_message(const char * fmt,va_list ap)101 expand_message(const char* fmt, va_list ap)
102 {
103   char* mbuf = 0;
104   std::string expanded_fmt = expand_format(fmt);
105   int nwr = vasprintf(&mbuf, expanded_fmt.c_str(), ap);
106   if (nwr == -1)
107     {
108       // memory allocation failed
109       go_be_error_at(Linemap::unknown_location(),
110                      "memory allocation failed in vasprintf");
111       go_assert(0);
112     }
113   std::string rval = std::string(mbuf);
114   free(mbuf);
115   return rval;
116 }
117 
118 #pragma GCC diagnostic pop
119 
120 static const char* cached_open_quote = NULL;
121 static const char* cached_close_quote = NULL;
122 
123 const char*
go_open_quote()124 go_open_quote()
125 {
126   if (cached_open_quote == NULL)
127     go_be_get_quotechars(&cached_open_quote, &cached_close_quote);
128   return cached_open_quote;
129 }
130 
131 const char*
go_close_quote()132 go_close_quote()
133 {
134   if (cached_close_quote == NULL)
135     go_be_get_quotechars(&cached_open_quote, &cached_close_quote);
136   return cached_close_quote;
137 }
138 
139 void
go_error_at(const Location location,const char * fmt,...)140 go_error_at(const Location location, const char* fmt, ...)
141 {
142   va_list ap;
143 
144   va_start(ap, fmt);
145   go_be_error_at(location, expand_message(fmt, ap));
146   va_end(ap);
147 }
148 
149 void
go_warning_at(const Location location,int opt,const char * fmt,...)150 go_warning_at(const Location location, int opt, const char* fmt, ...)
151 {
152   va_list ap;
153 
154   va_start(ap, fmt);
155   go_be_warning_at(location, opt, expand_message(fmt, ap));
156   va_end(ap);
157 }
158 
159 void
go_fatal_error(const Location location,const char * fmt,...)160 go_fatal_error(const Location location, const char* fmt, ...)
161 {
162   va_list ap;
163 
164   va_start(ap, fmt);
165   go_be_fatal_error(location, expand_message(fmt, ap));
166   va_end(ap);
167 }
168 
169 void
go_inform(const Location location,const char * fmt,...)170 go_inform(const Location location, const char* fmt, ...)
171 {
172   va_list ap;
173 
174   va_start(ap, fmt);
175   go_be_inform(location, expand_message(fmt, ap));
176   va_end(ap);
177 }
178