Branch data Line data Source code
1 : : /* Helper class for deferring path creation until a diagnostic is emitted.
2 : : Copyright (C) 2019-2025 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 under
8 : : the terms of the GNU General Public License as published by the Free
9 : : Software Foundation; either version 3, or (at your option) any later
10 : : version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : : 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 : :
22 : : #include "config.h"
23 : : #define INCLUDE_VECTOR
24 : : #include "system.h"
25 : : #include "coretypes.h"
26 : : #include "tree.h"
27 : : #include "version.h"
28 : : #include "intl.h"
29 : : #include "diagnostic.h"
30 : : #include "lazy-diagnostic-path.h"
31 : : #include "selftest.h"
32 : : #include "selftest-diagnostic.h"
33 : : #include "simple-diagnostic-path.h"
34 : : #include "gcc-rich-location.h"
35 : : #include "diagnostic-format-text.h"
36 : :
37 : : /* class lazy_diagnostic_path : public diagnostic_path. */
38 : :
39 : : /* Implementation of diagnostic_path vfuncs in terms of a lazily-generated
40 : : path. */
41 : :
42 : : unsigned
43 : 24 : lazy_diagnostic_path::num_events () const
44 : : {
45 : 24 : lazily_generate_path ();
46 : 24 : return m_inner_path->num_events ();
47 : : }
48 : :
49 : : const diagnostic_event &
50 : 36 : lazy_diagnostic_path::get_event (int idx) const
51 : : {
52 : 36 : lazily_generate_path ();
53 : 36 : return m_inner_path->get_event (idx);
54 : : }
55 : :
56 : : unsigned
57 : 4 : lazy_diagnostic_path::num_threads () const
58 : : {
59 : 4 : lazily_generate_path ();
60 : 4 : return m_inner_path->num_threads ();
61 : : }
62 : :
63 : : const diagnostic_thread &
64 : 4 : lazy_diagnostic_path::get_thread (diagnostic_thread_id_t idx) const
65 : : {
66 : 4 : lazily_generate_path ();
67 : 4 : return m_inner_path->get_thread (idx);
68 : : }
69 : :
70 : : bool
71 : 24 : lazy_diagnostic_path::same_function_p (int event_idx_a,
72 : : int event_idx_b) const
73 : : {
74 : 24 : lazily_generate_path ();
75 : 24 : return m_inner_path->same_function_p (event_idx_a, event_idx_b);
76 : : }
77 : :
78 : : void
79 : 92 : lazy_diagnostic_path::lazily_generate_path () const
80 : : {
81 : 92 : if (!m_inner_path)
82 : 8 : m_inner_path = make_inner_path ();
83 : 92 : gcc_assert (m_inner_path != nullptr);
84 : 92 : }
85 : :
86 : : #if CHECKING_P
87 : :
88 : : namespace selftest {
89 : :
90 : 8 : class test_lazy_path : public lazy_diagnostic_path
91 : : {
92 : : public:
93 : 12 : test_lazy_path (pretty_printer &pp)
94 : 12 : : lazy_diagnostic_path (m_logical_loc_mgr),
95 : 12 : m_pp (pp)
96 : : {
97 : : }
98 : 8 : std::unique_ptr<diagnostic_path> make_inner_path () const final override
99 : : {
100 : 8 : tree fntype_void_void
101 : 8 : = build_function_type_array (void_type_node, 0, NULL);
102 : 8 : tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
103 : 8 : auto path
104 : 8 : = std::make_unique<simple_diagnostic_path> (m_logical_loc_mgr,
105 : 8 : &m_pp);
106 : 8 : path->add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "first %qs", "free");
107 : 8 : path->add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "double %qs", "free");
108 : 8 : return path;
109 : 8 : }
110 : : private:
111 : : const tree_logical_location_manager m_logical_loc_mgr;
112 : : pretty_printer &m_pp;
113 : : };
114 : :
115 : : static void
116 : 4 : test_intraprocedural_path (pretty_printer *event_pp)
117 : : {
118 : 4 : test_lazy_path path (*event_pp);
119 : 4 : ASSERT_FALSE (path.generated_p ());
120 : 4 : ASSERT_EQ (path.num_events (), 2);
121 : 4 : ASSERT_TRUE (path.generated_p ());
122 : 4 : ASSERT_EQ (path.num_threads (), 1);
123 : 4 : ASSERT_FALSE (path.interprocedural_p ());
124 : 4 : ASSERT_STREQ (path.get_event (0).get_desc (*event_pp).get (),
125 : : "first `free'");
126 : 8 : ASSERT_STREQ (path.get_event (1).get_desc (*event_pp).get (),
127 : : "double `free'");
128 : 4 : }
129 : :
130 : : /* Implementation of diagnostic_option_manager for which all
131 : : options are disabled, for use in selftests.
132 : : Note that this is *not* called for diagnostic_option_id (0), which
133 : : means "always warn" */
134 : :
135 : 4 : class all_warnings_disabled : public diagnostic_option_manager
136 : : {
137 : : public:
138 : 4 : int option_enabled_p (diagnostic_option_id) const final override
139 : : {
140 : : /* Treat all options as disabled. */
141 : 4 : return 0;
142 : : }
143 : 0 : char *make_option_name (diagnostic_option_id,
144 : : diagnostic_t,
145 : : diagnostic_t) const final override
146 : : {
147 : 0 : return nullptr;
148 : : }
149 : 0 : char *make_option_url (diagnostic_option_id) const final override
150 : : {
151 : 0 : return nullptr;
152 : : }
153 : : };
154 : :
155 : : static void
156 : 4 : test_emission (pretty_printer *event_pp)
157 : : {
158 : 4 : struct test_rich_location : public gcc_rich_location
159 : : {
160 : 8 : test_rich_location (pretty_printer &event_pp)
161 : 8 : : gcc_rich_location (UNKNOWN_LOCATION),
162 : 8 : m_path (event_pp)
163 : : {
164 : 8 : set_path (&m_path);
165 : : }
166 : : test_lazy_path m_path;
167 : : };
168 : :
169 : : /* Verify that we don't bother generating the inner path if the warning
170 : : is skipped. */
171 : 4 : {
172 : 4 : test_diagnostic_context dc;
173 : 4 : dc.set_option_manager (std::make_unique<all_warnings_disabled> (), 0);
174 : :
175 : 4 : test_rich_location rich_loc (*event_pp);
176 : 4 : ASSERT_FALSE (rich_loc.m_path.generated_p ());
177 : :
178 : 4 : diagnostic_option_id option_id (42); // has to be non-zero
179 : 4 : bool emitted
180 : 4 : = dc.emit_diagnostic_with_group (DK_WARNING, rich_loc, nullptr,
181 : : option_id,
182 : : "this warning should be skipped");
183 : 4 : ASSERT_FALSE (emitted);
184 : 4 : ASSERT_FALSE (rich_loc.m_path.generated_p ());
185 : 4 : }
186 : :
187 : : /* Verify that we *do* generate the inner path for a diagnostic that
188 : : is emitted, such as an error. */
189 : 4 : {
190 : 4 : test_diagnostic_context dc;
191 : :
192 : 4 : test_rich_location rich_loc (*event_pp);
193 : 4 : ASSERT_FALSE (rich_loc.m_path.generated_p ());
194 : :
195 : 4 : bool emitted
196 : 4 : = dc.emit_diagnostic_with_group (DK_ERROR, rich_loc, nullptr, 0,
197 : : "this is a test");
198 : 4 : ASSERT_TRUE (emitted);
199 : 4 : ASSERT_TRUE (rich_loc.m_path.generated_p ());
200 : :
201 : : /* Verify that the path works as expected. */
202 : 4 : dc.set_path_format (DPF_INLINE_EVENTS);
203 : 4 : diagnostic_text_output_format sink (dc);
204 : 4 : pp_buffer (sink.get_printer ())->m_flush_p = false;
205 : 4 : sink.print_path (rich_loc.m_path);
206 : 4 : ASSERT_STREQ (pp_formatted_text (sink.get_printer ()),
207 : : " `foo': event 1\n"
208 : : " (1): first `free'\n"
209 : : " `foo': event 2\n"
210 : : " (2): double `free'\n");
211 : 4 : }
212 : 4 : }
213 : :
214 : : /* Run all of the selftests within this file. */
215 : :
216 : : void
217 : 4 : lazy_diagnostic_path_cc_tests ()
218 : : {
219 : : /* In a few places we use the global dc's printer to determine
220 : : colorization so ensure this off during the tests. */
221 : 4 : pretty_printer *global_pp = global_dc->get_reference_printer ();
222 : 4 : const bool saved_show_color = pp_show_color (global_pp);
223 : 4 : pp_show_color (global_pp) = false;
224 : :
225 : 4 : auto_fix_quotes fix_quotes;
226 : 4 : std::unique_ptr<pretty_printer> event_pp
227 : 4 : = std::unique_ptr<pretty_printer> (global_pp->clone ());
228 : :
229 : 4 : test_intraprocedural_path (event_pp.get ());
230 : 4 : test_emission (event_pp.get ());
231 : :
232 : 4 : pp_show_color (global_pp) = saved_show_color;
233 : 4 : }
234 : :
235 : : } // namespace selftest
236 : :
237 : : #endif /* #if CHECKING_P */
|