Line data Source code
1 : /* Helper class for deferring path creation until a diagnostic is emitted.
2 : Copyright (C) 2019-2026 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 "diagnostics/lazy-paths.h"
27 : #include "selftest.h"
28 : #include "diagnostics/selftest-context.h"
29 : #include "diagnostics/selftest-paths.h"
30 : #include "diagnostics/text-sink.h"
31 :
32 : using namespace diagnostics::paths;
33 :
34 : /* class lazy_path : public path. */
35 :
36 : /* Implementation of path vfuncs in terms of a lazily-generated
37 : path. */
38 :
39 : unsigned
40 5794 : lazy_path::num_events () const
41 : {
42 5794 : lazily_generate_path ();
43 5794 : return m_inner_path->num_events ();
44 : }
45 :
46 : const event &
47 166 : lazy_path::get_event (int idx) const
48 : {
49 166 : lazily_generate_path ();
50 166 : return m_inner_path->get_event (idx);
51 : }
52 :
53 : unsigned
54 4 : lazy_path::num_threads () const
55 : {
56 4 : lazily_generate_path ();
57 4 : return m_inner_path->num_threads ();
58 : }
59 :
60 : const thread &
61 19 : lazy_path::get_thread (thread_id_t idx) const
62 : {
63 19 : lazily_generate_path ();
64 19 : return m_inner_path->get_thread (idx);
65 : }
66 :
67 : bool
68 56 : lazy_path::same_function_p (int event_idx_a,
69 : int event_idx_b) const
70 : {
71 56 : lazily_generate_path ();
72 56 : return m_inner_path->same_function_p (event_idx_a, event_idx_b);
73 : }
74 :
75 : void
76 6039 : lazy_path::lazily_generate_path () const
77 : {
78 6039 : if (!m_inner_path)
79 5758 : m_inner_path = make_inner_path ();
80 6039 : gcc_assert (m_inner_path != nullptr);
81 6039 : }
82 :
83 : #if CHECKING_P
84 :
85 : namespace diagnostics {
86 : namespace selftest {
87 :
88 : using auto_fix_quotes = ::selftest::auto_fix_quotes;
89 :
90 : class test_lazy_path : public lazy_path
91 : {
92 : public:
93 12 : test_lazy_path (pretty_printer &pp)
94 12 : : lazy_path (m_logical_loc_mgr),
95 24 : m_pp (pp)
96 : {
97 : }
98 8 : std::unique_ptr<path> make_inner_path () const final override
99 : {
100 8 : auto path
101 8 : = std::make_unique<paths::selftest::test_path> (m_logical_loc_mgr,
102 8 : &m_pp);
103 8 : path->add_event (UNKNOWN_LOCATION, "foo", 0, "first %qs", "free");
104 8 : path->add_event (UNKNOWN_LOCATION, "foo", 0, "double %qs", "free");
105 8 : return path;
106 8 : }
107 : private:
108 : mutable logical_locations::selftest::test_manager m_logical_loc_mgr;
109 : pretty_printer &m_pp;
110 : };
111 :
112 : static void
113 4 : test_intraprocedural_path (pretty_printer *event_pp)
114 : {
115 4 : test_lazy_path path (*event_pp);
116 4 : ASSERT_FALSE (path.generated_p ());
117 4 : ASSERT_EQ (path.num_events (), 2);
118 4 : ASSERT_TRUE (path.generated_p ());
119 4 : ASSERT_EQ (path.num_threads (), 1);
120 4 : ASSERT_FALSE (path.interprocedural_p ());
121 4 : ASSERT_STREQ (path.get_event (0).get_desc (*event_pp).get (),
122 : "first `free'");
123 4 : ASSERT_STREQ (path.get_event (1).get_desc (*event_pp).get (),
124 : "double `free'");
125 4 : }
126 :
127 : /* Implementation of diagnostics::option_id_manager for which all
128 : options are disabled, for use in selftests.
129 : Note that this is *not* called for option_id (0), which
130 : means "always warn" */
131 :
132 4 : class all_warnings_disabled : public diagnostics::option_id_manager
133 : {
134 : public:
135 4 : int option_enabled_p (diagnostics::option_id) const final override
136 : {
137 : /* Treat all options as disabled. */
138 4 : return 0;
139 : }
140 0 : char *make_option_name (diagnostics::option_id,
141 : enum kind,
142 : enum kind) const final override
143 : {
144 0 : return nullptr;
145 : }
146 0 : char *make_option_url (diagnostics::option_id) const final override
147 : {
148 0 : return nullptr;
149 : }
150 : };
151 :
152 : static void
153 4 : test_emission (pretty_printer *event_pp)
154 : {
155 8 : struct test_rich_location : public rich_location
156 : {
157 8 : test_rich_location (pretty_printer &event_pp)
158 8 : : rich_location (line_table, UNKNOWN_LOCATION),
159 8 : m_path (event_pp)
160 : {
161 8 : set_path (&m_path);
162 8 : }
163 : test_lazy_path m_path;
164 : };
165 :
166 : /* Verify that we don't bother generating the inner path if the warning
167 : is skipped. */
168 4 : {
169 4 : test_context dc;
170 4 : dc.set_option_id_manager (std::make_unique<all_warnings_disabled> (), 0);
171 :
172 4 : test_rich_location rich_loc (*event_pp);
173 4 : ASSERT_FALSE (rich_loc.m_path.generated_p ());
174 :
175 4 : diagnostics::option_id opt_id (42); // has to be non-zero
176 4 : bool emitted
177 4 : = dc.emit_diagnostic_with_group (kind::warning, rich_loc, nullptr,
178 : opt_id,
179 : "this warning should be skipped");
180 4 : ASSERT_FALSE (emitted);
181 4 : ASSERT_FALSE (rich_loc.m_path.generated_p ());
182 4 : }
183 :
184 : /* Verify that we *do* generate the inner path for a diagnostic that
185 : is emitted, such as an error. */
186 4 : {
187 4 : test_context dc;
188 :
189 4 : test_rich_location rich_loc (*event_pp);
190 4 : ASSERT_FALSE (rich_loc.m_path.generated_p ());
191 :
192 4 : bool emitted
193 4 : = dc.emit_diagnostic_with_group (kind::error, rich_loc, nullptr, 0,
194 : "this is a test");
195 4 : ASSERT_TRUE (emitted);
196 4 : ASSERT_TRUE (rich_loc.m_path.generated_p ());
197 :
198 : /* Verify that the path works as expected. */
199 4 : dc.set_path_format (DPF_INLINE_EVENTS);
200 4 : diagnostics::text_sink sink_ (dc);
201 4 : pp_buffer (sink_.get_printer ())->m_flush_p = false;
202 4 : sink_.print_path (rich_loc.m_path);
203 4 : ASSERT_STREQ (pp_formatted_text (sink_.get_printer ()),
204 : " `foo': event 1\n"
205 : " (1): first `free'\n"
206 : " `foo': event 2\n"
207 : " (2): double `free'\n");
208 8 : }
209 4 : }
210 :
211 : /* Run all of the selftests within this file. */
212 :
213 : void
214 4 : lazy_paths_cc_tests ()
215 : {
216 : /* In a few places we use the global dc's printer to determine
217 : colorization so ensure this off during the tests. */
218 4 : pretty_printer *global_pp = global_dc->get_reference_printer ();
219 4 : const bool saved_show_color = pp_show_color (global_pp);
220 4 : pp_show_color (global_pp) = false;
221 :
222 4 : auto_fix_quotes fix_quotes;
223 4 : std::unique_ptr<pretty_printer> event_pp
224 4 : = std::unique_ptr<pretty_printer> (global_pp->clone ());
225 :
226 4 : test_intraprocedural_path (event_pp.get ());
227 4 : test_emission (event_pp.get ());
228 :
229 4 : pp_show_color (global_pp) = saved_show_color;
230 4 : }
231 :
232 : } // namespace diagnostics::selftest
233 : } // namespace diagnostics
234 :
235 : #endif /* #if CHECKING_P */
|