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 
24 namespace ana {
25 
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 {
event_descevent_desc35   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 {
state_changestate_change47   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 
is_global_pstate_change60   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 {
call_with_statecall_with_state74   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 {
return_of_statereturn_of_state95   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 {
final_eventfinal_event114   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:
~pending_diagnostic()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 
equal_p(const pending_diagnostic & other)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 
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 
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 
describe_state_change(const evdesc::state_change &)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 
describe_call_with_state(const evdesc::call_with_state &)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 
describe_return_of_state(const evdesc::return_of_state &)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 
describe_final_event(const evdesc::final_event &)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:
subclass_equal_p(const pending_diagnostic & base_other)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 
272 } // namespace ana
273 
274 #endif /* GCC_ANALYZER_PENDING_DIAGNOSTIC_H */
275