1 | /* Classes for analyzer diagnostics. |
2 | Copyright (C) 2019-2024 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 | #include "config.h" |
22 | #define INCLUDE_MEMORY |
23 | #include "system.h" |
24 | #include "coretypes.h" |
25 | #include "tree.h" |
26 | #include "intl.h" |
27 | #include "diagnostic.h" |
28 | #include "analyzer/analyzer.h" |
29 | #include "diagnostic-event-id.h" |
30 | #include "analyzer/analyzer-logging.h" |
31 | #include "analyzer/sm.h" |
32 | #include "diagnostic-event-id.h" |
33 | #include "analyzer/sm.h" |
34 | #include "analyzer/pending-diagnostic.h" |
35 | #include "analyzer/diagnostic-manager.h" |
36 | #include "analyzer/call-string.h" |
37 | #include "analyzer/program-point.h" |
38 | #include "analyzer/store.h" |
39 | #include "analyzer/region-model.h" |
40 | #include "cpplib.h" |
41 | #include "digraph.h" |
42 | #include "ordered-hash-map.h" |
43 | #include "cfg.h" |
44 | #include "basic-block.h" |
45 | #include "gimple.h" |
46 | #include "gimple-iterator.h" |
47 | #include "cgraph.h" |
48 | #include "analyzer/supergraph.h" |
49 | #include "analyzer/program-state.h" |
50 | #include "analyzer/exploded-graph.h" |
51 | #include "diagnostic-path.h" |
52 | #include "analyzer/checker-path.h" |
53 | #include "make-unique.h" |
54 | |
55 | #if ENABLE_ANALYZER |
56 | |
57 | namespace ana { |
58 | |
59 | /* struct interesting_t. */ |
60 | |
61 | /* Mark the creation of REG as being interesting. */ |
62 | |
63 | void |
64 | interesting_t::add_region_creation (const region *reg) |
65 | { |
66 | gcc_assert (reg); |
67 | m_region_creation.safe_push (obj: reg); |
68 | } |
69 | |
70 | void |
71 | interesting_t::dump_to_pp (pretty_printer *pp, bool simple) const |
72 | { |
73 | pp_string (pp, "{ region creation: [" ); |
74 | unsigned i; |
75 | const region *reg; |
76 | FOR_EACH_VEC_ELT (m_region_creation, i, reg) |
77 | { |
78 | if (i > 0) |
79 | pp_string (pp, ", " ); |
80 | reg->dump_to_pp (pp, simple); |
81 | } |
82 | pp_string (pp, "]}" ); |
83 | } |
84 | |
85 | /* Generate a label_text by printing FMT. |
86 | |
87 | Use a clone of the global_dc for formatting callbacks. |
88 | |
89 | Use this evdesc::event_desc's m_colorize flag to control colorization |
90 | (so that e.g. we can disable it for JSON output). */ |
91 | |
92 | label_text |
93 | evdesc::event_desc::formatted_print (const char *fmt, ...) const |
94 | { |
95 | pretty_printer *pp = global_dc->printer->clone (); |
96 | |
97 | pp_show_color (pp) = m_colorize; |
98 | |
99 | rich_location rich_loc (line_table, UNKNOWN_LOCATION); |
100 | va_list ap; |
101 | va_start (ap, fmt); |
102 | text_info ti (_(fmt), &ap, 0, nullptr, &rich_loc); |
103 | pp_format (pp, &ti); |
104 | pp_output_formatted_text (pp); |
105 | va_end (ap); |
106 | |
107 | label_text result = label_text::take (buffer: xstrdup (pp_formatted_text (pp))); |
108 | delete pp; |
109 | return result; |
110 | } |
111 | |
112 | /* class diagnostic_emission_context. */ |
113 | |
114 | /* Get the pending_diagnostic being emitted. */ |
115 | |
116 | const pending_diagnostic & |
117 | diagnostic_emission_context::get_pending_diagnostic () const |
118 | { |
119 | return *m_sd.m_d.get (); |
120 | } |
121 | |
122 | /* Emit a warning, using the rich_location, metadata, and the |
123 | pending_diagnostic's option. */ |
124 | |
125 | bool |
126 | diagnostic_emission_context::warn (const char *gmsgid, ...) |
127 | { |
128 | const pending_diagnostic &pd = get_pending_diagnostic (); |
129 | auto_diagnostic_group d; |
130 | va_list ap; |
131 | va_start (ap, gmsgid); |
132 | const bool result = emit_diagnostic_valist_meta (DK_WARNING, |
133 | &m_rich_loc, &m_metadata, |
134 | pd.get_controlling_option (), |
135 | gmsgid, &ap); |
136 | va_end (ap); |
137 | return result; |
138 | } |
139 | |
140 | /* Emit a note, using the rich_location and metadata (and the |
141 | pending_diagnostic's option). */ |
142 | |
143 | void |
144 | diagnostic_emission_context::inform (const char *gmsgid, ...) |
145 | { |
146 | const pending_diagnostic &pd = get_pending_diagnostic (); |
147 | auto_diagnostic_group d; |
148 | va_list ap; |
149 | va_start (ap, gmsgid); |
150 | emit_diagnostic_valist_meta (DK_NOTE, |
151 | &m_rich_loc, &m_metadata, |
152 | pd.get_controlling_option (), |
153 | gmsgid, &ap); |
154 | va_end (ap); |
155 | } |
156 | |
157 | /* Return true if T1 and T2 are "the same" for the purposes of |
158 | diagnostic deduplication. */ |
159 | |
160 | bool |
161 | pending_diagnostic::same_tree_p (tree t1, tree t2) |
162 | { |
163 | return simple_cst_equal (t1, t2) == 1; |
164 | } |
165 | |
166 | /* Return true iff IDENT is STR. */ |
167 | |
168 | static bool |
169 | ht_ident_eq (ht_identifier ident, const char *str) |
170 | { |
171 | return (strlen (s: str) == ident.len |
172 | && 0 == strcmp (s1: str, s2: (const char *)ident.str)); |
173 | } |
174 | |
175 | /* Return true if we should show the expansion location rather than unwind |
176 | within MACRO. */ |
177 | |
178 | static bool |
179 | fixup_location_in_macro_p (cpp_hashnode *macro) |
180 | { |
181 | ht_identifier ident = macro->ident; |
182 | |
183 | /* Don't unwind inside "alloca" macro, so that we don't suppress warnings |
184 | from it (due to being in system headers). */ |
185 | if (ht_ident_eq (ident, str: "alloca" )) |
186 | return true; |
187 | |
188 | /* Don't unwind inside <stdarg.h> macros, so that we don't suppress warnings |
189 | from them (due to being in system headers). */ |
190 | if (ht_ident_eq (ident, str: "va_start" ) |
191 | || ht_ident_eq (ident, str: "va_copy" ) |
192 | || ht_ident_eq (ident, str: "va_arg" ) |
193 | || ht_ident_eq (ident, str: "va_end" )) |
194 | return true; |
195 | return false; |
196 | } |
197 | |
198 | /* Base implementation of pending_diagnostic::fixup_location. |
199 | Don't unwind inside macros for which fixup_location_in_macro_p is true. */ |
200 | |
201 | location_t |
202 | pending_diagnostic::fixup_location (location_t loc, bool) const |
203 | { |
204 | if (linemap_location_from_macro_expansion_p (line_table, loc)) |
205 | { |
206 | line_map *map |
207 | = const_cast <line_map *> (linemap_lookup (line_table, loc)); |
208 | const line_map_macro *macro_map = linemap_check_macro (map); |
209 | if (fixup_location_in_macro_p (macro: macro_map->macro)) |
210 | loc = linemap_resolve_location (line_table, loc, |
211 | lrk: LRK_MACRO_EXPANSION_POINT, NULL); |
212 | } |
213 | return loc; |
214 | } |
215 | |
216 | /* Base implementation of pending_diagnostic::add_function_entry_event. |
217 | Add a function_entry_event to EMISSION_PATH. */ |
218 | |
219 | void |
220 | pending_diagnostic::add_function_entry_event (const exploded_edge &eedge, |
221 | checker_path *emission_path) |
222 | { |
223 | const exploded_node *dst_node = eedge.m_dest; |
224 | const program_point &dst_point = dst_node->get_point (); |
225 | emission_path->add_event (event: make_unique<function_entry_event> (args: dst_point)); |
226 | } |
227 | |
228 | /* Base implementation of pending_diagnostic::add_call_event. |
229 | Add a call_event to EMISSION_PATH. */ |
230 | |
231 | void |
232 | pending_diagnostic::add_call_event (const exploded_edge &eedge, |
233 | checker_path *emission_path) |
234 | { |
235 | const exploded_node *src_node = eedge.m_src; |
236 | const program_point &src_point = src_node->get_point (); |
237 | const int src_stack_depth = src_point.get_stack_depth (); |
238 | const gimple *last_stmt = src_point.get_supernode ()->get_last_stmt (); |
239 | emission_path->add_event |
240 | (event: make_unique<call_event> (args: eedge, |
241 | args: event_loc_info (last_stmt |
242 | ? last_stmt->location |
243 | : UNKNOWN_LOCATION, |
244 | src_point.get_fndecl (), |
245 | src_stack_depth))); |
246 | } |
247 | |
248 | /* Base implementation of pending_diagnostic::add_region_creation_events. |
249 | See the comment for class region_creation_event. */ |
250 | |
251 | void |
252 | pending_diagnostic::add_region_creation_events (const region *reg, |
253 | tree capacity, |
254 | const event_loc_info &loc_info, |
255 | checker_path &emission_path) |
256 | { |
257 | emission_path.add_event |
258 | (event: make_unique<region_creation_event_memory_space> (args: reg->get_memory_space (), |
259 | args: loc_info)); |
260 | |
261 | if (capacity) |
262 | emission_path.add_event |
263 | (event: make_unique<region_creation_event_capacity> (args&: capacity, args: loc_info)); |
264 | } |
265 | |
266 | /* Base implementation of pending_diagnostic::add_final_event. |
267 | Add a warning_event to the end of EMISSION_PATH. */ |
268 | |
269 | void |
270 | pending_diagnostic::add_final_event (const state_machine *sm, |
271 | const exploded_node *enode, |
272 | const gimple *stmt, |
273 | tree var, state_machine::state_t state, |
274 | checker_path *emission_path) |
275 | { |
276 | emission_path->add_event |
277 | (event: make_unique<warning_event> |
278 | (args: event_loc_info (get_stmt_location (stmt, fun: enode->get_function ()), |
279 | enode->get_function ()->decl, |
280 | enode->get_stack_depth ()), |
281 | args&: enode, |
282 | args&: sm, args&: var, args&: state)); |
283 | } |
284 | |
285 | } // namespace ana |
286 | |
287 | #endif /* #if ENABLE_ANALYZER */ |
288 | |