]>
Commit | Line | Data |
---|---|---|
757bf1df DM |
1 | /* Classes for analyzer diagnostics. |
2 | Copyright (C) 2019-2020 Free Software Foundation, Inc. | |
3 | Contributed by David Malcolm <dmalcolm@redhat.com>. | |
4 | ||
5 | This file is part of GCC. | |
6 | ||
7 | GCC is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 3, or (at your option) | |
10 | any later version. | |
11 | ||
12 | GCC is distributed in the hope that it will be useful, but | |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with GCC; see the file COPYING3. If not see | |
19 | <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | #ifndef GCC_ANALYZER_PENDING_DIAGNOSTIC_H | |
22 | #define GCC_ANALYZER_PENDING_DIAGNOSTIC_H | |
23 | ||
75038aa6 DM |
24 | namespace ana { |
25 | ||
757bf1df DM |
26 | /* Various bundles of information used for generating more precise |
27 | messages for events within a diagnostic_path, for passing to the | |
28 | various "describe_*" vfuncs of pending_diagnostic. See those | |
29 | for more information. */ | |
30 | ||
31 | namespace evdesc { | |
32 | ||
33 | struct event_desc | |
34 | { | |
35 | event_desc (bool colorize) : m_colorize (colorize) {} | |
36 | ||
37 | label_text formatted_print (const char *fmt, ...) const | |
38 | ATTRIBUTE_GCC_DIAG(2,3); | |
39 | ||
40 | bool m_colorize; | |
41 | }; | |
42 | ||
43 | /* For use by pending_diagnostic::describe_state_change. */ | |
44 | ||
45 | struct state_change : public event_desc | |
46 | { | |
47 | state_change (bool colorize, | |
48 | tree expr, | |
49 | tree origin, | |
50 | state_machine::state_t old_state, | |
51 | state_machine::state_t new_state, | |
52 | diagnostic_event_id_t event_id, | |
53 | const state_change_event &event) | |
54 | : event_desc (colorize), | |
55 | m_expr (expr), m_origin (origin), | |
56 | m_old_state (old_state), m_new_state (new_state), | |
57 | m_event_id (event_id), m_event (event) | |
58 | {} | |
59 | ||
60 | bool is_global_p () const { return m_expr == NULL_TREE; } | |
61 | ||
62 | tree m_expr; | |
63 | tree m_origin; | |
64 | state_machine::state_t m_old_state; | |
65 | state_machine::state_t m_new_state; | |
66 | diagnostic_event_id_t m_event_id; | |
67 | const state_change_event &m_event; | |
68 | }; | |
69 | ||
70 | /* For use by pending_diagnostic::describe_call_with_state. */ | |
71 | ||
72 | struct call_with_state : public event_desc | |
73 | { | |
74 | call_with_state (bool colorize, | |
75 | tree caller_fndecl, tree callee_fndecl, | |
76 | tree expr, state_machine::state_t state) | |
77 | : event_desc (colorize), | |
78 | m_caller_fndecl (caller_fndecl), | |
79 | m_callee_fndecl (callee_fndecl), | |
80 | m_expr (expr), | |
81 | m_state (state) | |
82 | { | |
83 | } | |
84 | ||
85 | tree m_caller_fndecl; | |
86 | tree m_callee_fndecl; | |
87 | tree m_expr; | |
88 | state_machine::state_t m_state; | |
89 | }; | |
90 | ||
91 | /* For use by pending_diagnostic::describe_return_of_state. */ | |
92 | ||
93 | struct return_of_state : public event_desc | |
94 | { | |
95 | return_of_state (bool colorize, | |
96 | tree caller_fndecl, tree callee_fndecl, | |
97 | state_machine::state_t state) | |
98 | : event_desc (colorize), | |
99 | m_caller_fndecl (caller_fndecl), | |
100 | m_callee_fndecl (callee_fndecl), | |
101 | m_state (state) | |
102 | { | |
103 | } | |
104 | ||
105 | tree m_caller_fndecl; | |
106 | tree m_callee_fndecl; | |
107 | state_machine::state_t m_state; | |
108 | }; | |
109 | ||
110 | /* For use by pending_diagnostic::describe_final_event. */ | |
111 | ||
112 | struct final_event : public event_desc | |
113 | { | |
114 | final_event (bool colorize, | |
115 | tree expr, state_machine::state_t state) | |
116 | : event_desc (colorize), | |
117 | m_expr (expr), m_state (state) | |
118 | {} | |
119 | ||
120 | tree m_expr; | |
121 | state_machine::state_t m_state; | |
122 | }; | |
123 | ||
124 | } /* end of namespace evdesc */ | |
125 | ||
126 | /* An abstract base class for capturing information about a diagnostic in | |
127 | a form that is ready to emit at a later point (or be rejected). | |
128 | Each kind of diagnostic will have a concrete subclass of | |
129 | pending_diagnostic. | |
130 | ||
131 | Normally, gcc diagnostics are emitted using va_list, which can't be | |
132 | portably stored for later use, so we have to use an "emit" virtual | |
133 | function. | |
134 | ||
135 | This class also supports comparison, so that multiple pending_diagnostic | |
136 | instances can be de-duplicated. | |
137 | ||
138 | As well as emitting a diagnostic, the class has various "precision of | |
139 | wording" virtual functions, for generating descriptions for events | |
140 | within a diagnostic_path. These are optional, but implementing these | |
141 | allows for more precise wordings than the more generic | |
142 | implementation. */ | |
143 | ||
144 | class pending_diagnostic | |
145 | { | |
146 | public: | |
147 | virtual ~pending_diagnostic () {} | |
148 | ||
149 | /* Vfunc for emitting the diagnostic. The rich_location will have been | |
150 | populated with a diagnostic_path. | |
151 | Return true if a diagnostic is actually emitted. */ | |
152 | virtual bool emit (rich_location *) = 0; | |
153 | ||
154 | /* Hand-coded RTTI: get an ID for the subclass. */ | |
155 | virtual const char *get_kind () const = 0; | |
156 | ||
157 | /* Compare for equality with OTHER, which might be of a different | |
158 | subclass. */ | |
159 | ||
160 | bool equal_p (const pending_diagnostic &other) | |
161 | { | |
162 | /* Check for pointer equality on the IDs from get_kind. */ | |
163 | if (get_kind () != other.get_kind ()) | |
164 | return false; | |
165 | /* Call vfunc now we know they have the same ID: */ | |
166 | return subclass_equal_p (other); | |
167 | } | |
168 | ||
169 | /* A vfunc for testing for equality, where we've already | |
170 | checked they have the same ID. See pending_diagnostic_subclass | |
171 | below for a convenience subclass for implementing this. */ | |
172 | virtual bool subclass_equal_p (const pending_diagnostic &other) const = 0; | |
173 | ||
14f9d7b9 DM |
174 | /* Return true if T1 and T2 are "the same" for the purposes of |
175 | diagnostic deduplication. */ | |
176 | static bool same_tree_p (tree t1, tree t2); | |
177 | ||
757bf1df DM |
178 | /* For greatest precision-of-wording, the various following "describe_*" |
179 | virtual functions give the pending diagnostic a way to describe events | |
180 | in a diagnostic_path in terms that make sense for that diagnostic. | |
181 | ||
182 | In each case, return a non-NULL label_text to give the event a custom | |
183 | description; NULL otherwise (falling back on a more generic | |
184 | description). */ | |
185 | ||
186 | /* Precision-of-wording vfunc for describing a critical state change | |
187 | within the diagnostic_path. | |
188 | ||
189 | For example, a double-free diagnostic might use the descriptions: | |
190 | - "first 'free' happens here" | |
191 | - "second 'free' happens here" | |
192 | for the pertinent events, whereas a use-after-free might use the | |
193 | descriptions: | |
194 | - "freed here" | |
195 | - "use after free here" | |
196 | Note how in both cases the first event is a "free": the best | |
197 | description to use depends on the diagnostic. */ | |
198 | ||
199 | virtual label_text describe_state_change (const evdesc::state_change &) | |
200 | { | |
201 | /* Default no-op implementation. */ | |
202 | return label_text (); | |
203 | } | |
204 | ||
205 | /* Precision-of-wording vfunc for describing an interprocedural call | |
206 | carrying critial state for the diagnostic, from caller to callee. | |
207 | ||
208 | For example a double-free diagnostic might use: | |
209 | - "passing freed pointer 'ptr' in call to 'deallocator' from 'test'" | |
210 | to make it clearer how the freed value moves from caller to | |
211 | callee. */ | |
212 | ||
213 | virtual label_text describe_call_with_state (const evdesc::call_with_state &) | |
214 | { | |
215 | /* Default no-op implementation. */ | |
216 | return label_text (); | |
217 | } | |
218 | ||
219 | /* Precision-of-wording vfunc for describing an interprocedural return | |
220 | within the diagnostic_path that carries critial state for the | |
221 | diagnostic, from callee back to caller. | |
222 | ||
223 | For example, a deref-of-unchecked-malloc diagnostic might use: | |
224 | - "returning possibly-NULL pointer to 'make_obj' from 'allocator'" | |
225 | to make it clearer how the unchecked value moves from callee | |
226 | back to caller. */ | |
227 | ||
228 | virtual label_text describe_return_of_state (const evdesc::return_of_state &) | |
229 | { | |
230 | /* Default no-op implementation. */ | |
231 | return label_text (); | |
232 | } | |
233 | ||
234 | /* Precision-of-wording vfunc for describing the final event within a | |
235 | diagnostic_path. | |
236 | ||
237 | For example a double-free diagnostic might use: | |
238 | - "second 'free' here; first 'free' was at (3)" | |
239 | and a use-after-free might use | |
240 | - "use after 'free' here; memory was freed at (2)". */ | |
241 | ||
242 | virtual label_text describe_final_event (const evdesc::final_event &) | |
243 | { | |
244 | /* Default no-op implementation. */ | |
245 | return label_text (); | |
246 | } | |
247 | ||
248 | /* End of precision-of-wording vfuncs. */ | |
249 | }; | |
250 | ||
251 | /* A template to make it easier to make subclasses of pending_diagnostic. | |
252 | ||
253 | This uses the curiously-recurring template pattern, to implement | |
254 | pending_diagnostic::subclass_equal_p by casting and calling | |
255 | the operator== | |
256 | ||
257 | This assumes that BASE_OTHER has already been checked to have | |
258 | been of the same subclass (which pending_diagnostic::equal_p does). */ | |
259 | ||
260 | template <class Subclass> | |
261 | class pending_diagnostic_subclass : public pending_diagnostic | |
262 | { | |
263 | public: | |
264 | bool subclass_equal_p (const pending_diagnostic &base_other) const | |
265 | FINAL OVERRIDE | |
266 | { | |
267 | const Subclass &other = (const Subclass &)base_other; | |
268 | return *(const Subclass*)this == other; | |
269 | } | |
270 | }; | |
271 | ||
75038aa6 DM |
272 | } // namespace ana |
273 | ||
757bf1df | 274 | #endif /* GCC_ANALYZER_PENDING_DIAGNOSTIC_H */ |