/* Helper class for deferring path creation until a diagnostic is emitted. Copyright (C) 2019-2025 Free Software Foundation, Inc. Contributed by David Malcolm This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include "config.h" #define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "diagnostic.h" #include "diagnostics/lazy-paths.h" #include "selftest.h" #include "diagnostics/selftest-context.h" #include "diagnostics/selftest-paths.h" #include "diagnostics/text-sink.h" using namespace diagnostics::paths; /* class lazy_path : public path. */ /* Implementation of path vfuncs in terms of a lazily-generated path. */ unsigned lazy_path::num_events () const { lazily_generate_path (); return m_inner_path->num_events (); } const event & lazy_path::get_event (int idx) const { lazily_generate_path (); return m_inner_path->get_event (idx); } unsigned lazy_path::num_threads () const { lazily_generate_path (); return m_inner_path->num_threads (); } const thread & lazy_path::get_thread (thread_id_t idx) const { lazily_generate_path (); return m_inner_path->get_thread (idx); } bool lazy_path::same_function_p (int event_idx_a, int event_idx_b) const { lazily_generate_path (); return m_inner_path->same_function_p (event_idx_a, event_idx_b); } void lazy_path::lazily_generate_path () const { if (!m_inner_path) m_inner_path = make_inner_path (); gcc_assert (m_inner_path != nullptr); } #if CHECKING_P namespace diagnostics { namespace selftest { using auto_fix_quotes = ::selftest::auto_fix_quotes; class test_lazy_path : public lazy_path { public: test_lazy_path (pretty_printer &pp) : lazy_path (m_logical_loc_mgr), m_pp (pp) { } std::unique_ptr make_inner_path () const final override { auto path = std::make_unique (m_logical_loc_mgr, &m_pp); path->add_event (UNKNOWN_LOCATION, "foo", 0, "first %qs", "free"); path->add_event (UNKNOWN_LOCATION, "foo", 0, "double %qs", "free"); return path; } private: mutable logical_locations::selftest::test_manager m_logical_loc_mgr; pretty_printer &m_pp; }; static void test_intraprocedural_path (pretty_printer *event_pp) { test_lazy_path path (*event_pp); ASSERT_FALSE (path.generated_p ()); ASSERT_EQ (path.num_events (), 2); ASSERT_TRUE (path.generated_p ()); ASSERT_EQ (path.num_threads (), 1); ASSERT_FALSE (path.interprocedural_p ()); ASSERT_STREQ (path.get_event (0).get_desc (*event_pp).get (), "first `free'"); ASSERT_STREQ (path.get_event (1).get_desc (*event_pp).get (), "double `free'"); } /* Implementation of diagnostics::option_manager for which all options are disabled, for use in selftests. Note that this is *not* called for option_id (0), which means "always warn" */ class all_warnings_disabled : public diagnostics::option_manager { public: int option_enabled_p (diagnostics::option_id) const final override { /* Treat all options as disabled. */ return 0; } char *make_option_name (diagnostics::option_id, enum kind, enum kind) const final override { return nullptr; } char *make_option_url (diagnostics::option_id) const final override { return nullptr; } }; static void test_emission (pretty_printer *event_pp) { struct test_rich_location : public rich_location { test_rich_location (pretty_printer &event_pp) : rich_location (line_table, UNKNOWN_LOCATION), m_path (event_pp) { set_path (&m_path); } test_lazy_path m_path; }; /* Verify that we don't bother generating the inner path if the warning is skipped. */ { test_context dc; dc.set_option_manager (std::make_unique (), 0); test_rich_location rich_loc (*event_pp); ASSERT_FALSE (rich_loc.m_path.generated_p ()); diagnostics::option_id opt_id (42); // has to be non-zero bool emitted = dc.emit_diagnostic_with_group (kind::warning, rich_loc, nullptr, opt_id, "this warning should be skipped"); ASSERT_FALSE (emitted); ASSERT_FALSE (rich_loc.m_path.generated_p ()); } /* Verify that we *do* generate the inner path for a diagnostic that is emitted, such as an error. */ { test_context dc; test_rich_location rich_loc (*event_pp); ASSERT_FALSE (rich_loc.m_path.generated_p ()); bool emitted = dc.emit_diagnostic_with_group (kind::error, rich_loc, nullptr, 0, "this is a test"); ASSERT_TRUE (emitted); ASSERT_TRUE (rich_loc.m_path.generated_p ()); /* Verify that the path works as expected. */ dc.set_path_format (DPF_INLINE_EVENTS); diagnostics::text_sink sink_ (dc); pp_buffer (sink_.get_printer ())->m_flush_p = false; sink_.print_path (rich_loc.m_path); ASSERT_STREQ (pp_formatted_text (sink_.get_printer ()), " `foo': event 1\n" " (1): first `free'\n" " `foo': event 2\n" " (2): double `free'\n"); } } /* Run all of the selftests within this file. */ void lazy_paths_cc_tests () { /* In a few places we use the global dc's printer to determine colorization so ensure this off during the tests. */ pretty_printer *global_pp = global_dc->get_reference_printer (); const bool saved_show_color = pp_show_color (global_pp); pp_show_color (global_pp) = false; auto_fix_quotes fix_quotes; std::unique_ptr event_pp = std::unique_ptr (global_pp->clone ()); test_intraprocedural_path (event_pp.get ()); test_emission (event_pp.get ()); pp_show_color (global_pp) = saved_show_color; } } // namespace diagnostics::selftest } // namespace diagnostics #endif /* #if CHECKING_P */