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 "make-unique.h"
32 : : #include "selftest.h"
33 : : #include "selftest-diagnostic.h"
34 : : #include "simple-diagnostic-path.h"
35 : : #include "gcc-rich-location.h"
36 : : #include "diagnostic-format-text.h"
37 : :
38 : : /* class lazy_diagnostic_path : public diagnostic_path. */
39 : :
40 : : /* Implementation of diagnostic_path vfuncs in terms of a lazily-generated
41 : : path. */
42 : :
43 : : unsigned
44 : 24 : lazy_diagnostic_path::num_events () const
45 : : {
46 : 24 : lazily_generate_path ();
47 : 24 : return m_inner_path->num_events ();
48 : : }
49 : :
50 : : const diagnostic_event &
51 : 36 : lazy_diagnostic_path::get_event (int idx) const
52 : : {
53 : 36 : lazily_generate_path ();
54 : 36 : return m_inner_path->get_event (idx);
55 : : }
56 : :
57 : : unsigned
58 : 4 : lazy_diagnostic_path::num_threads () const
59 : : {
60 : 4 : lazily_generate_path ();
61 : 4 : return m_inner_path->num_threads ();
62 : : }
63 : :
64 : : const diagnostic_thread &
65 : 4 : lazy_diagnostic_path::get_thread (diagnostic_thread_id_t idx) const
66 : : {
67 : 4 : lazily_generate_path ();
68 : 4 : return m_inner_path->get_thread (idx);
69 : : }
70 : :
71 : : bool
72 : 24 : lazy_diagnostic_path::same_function_p (int event_idx_a,
73 : : int event_idx_b) const
74 : : {
75 : 24 : lazily_generate_path ();
76 : 24 : return m_inner_path->same_function_p (event_idx_a, event_idx_b);
77 : : }
78 : :
79 : : void
80 : 92 : lazy_diagnostic_path::lazily_generate_path () const
81 : : {
82 : 92 : if (!m_inner_path)
83 : 8 : m_inner_path = make_inner_path ();
84 : 92 : gcc_assert (m_inner_path != nullptr);
85 : 92 : }
86 : :
87 : : #if CHECKING_P
88 : :
89 : : namespace selftest {
90 : :
91 : 8 : class test_lazy_path : public lazy_diagnostic_path
92 : : {
93 : : public:
94 : 12 : test_lazy_path (pretty_printer &pp)
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 = ::make_unique<simple_diagnostic_path> (&m_pp);
104 : 8 : path->add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "first %qs", "free");
105 : 8 : path->add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "double %qs", "free");
106 : 8 : return path;
107 : 8 : }
108 : : private:
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 : 8 : ASSERT_STREQ (path.get_event (1).get_desc (*event_pp).get (),
124 : : "double `free'");
125 : 4 : }
126 : :
127 : : /* Implementation of diagnostic_option_manager for which all
128 : : options are disabled, for use in selftests.
129 : : Note that this is *not* called for diagnostic_option_id (0), which
130 : : means "always warn" */
131 : :
132 : 4 : class all_warnings_disabled : public diagnostic_option_manager
133 : : {
134 : : public:
135 : 4 : int option_enabled_p (diagnostic_option_id) const final override
136 : : {
137 : : /* Treat all options as disabled. */
138 : 4 : return 0;
139 : : }
140 : 0 : char *make_option_name (diagnostic_option_id,
141 : : diagnostic_t,
142 : : diagnostic_t) const final override
143 : : {
144 : 0 : return nullptr;
145 : : }
146 : 0 : char *make_option_url (diagnostic_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 : 4 : struct test_rich_location : public gcc_rich_location
156 : : {
157 : 8 : test_rich_location (pretty_printer &event_pp)
158 : 8 : : gcc_rich_location (UNKNOWN_LOCATION),
159 : 8 : m_path (event_pp)
160 : : {
161 : 8 : set_path (&m_path);
162 : : }
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_diagnostic_context dc;
170 : 4 : dc.set_option_manager (::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 : diagnostic_option_id option_id (42); // has to be non-zero
176 : 4 : bool emitted
177 : 4 : = dc.emit_diagnostic_with_group (DK_WARNING, rich_loc, nullptr,
178 : : option_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_diagnostic_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 (DK_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 : diagnostic_text_output_format 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 : 4 : }
209 : 4 : }
210 : :
211 : : /* Run all of the selftests within this file. */
212 : :
213 : : void
214 : 4 : lazy_diagnostic_path_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 selftest
233 : :
234 : : #endif /* #if CHECKING_P */
|