blob: bc769c45766e55ffa7997b1c42ca581280329dd4 [file] [log] [blame]
/* Paths through the code associated with a diagnostic.
Copyright (C) 2019-2025 Free Software Foundation, Inc.
Contributed by David Malcolm <dmalcolm@redhat.com>
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
<http://www.gnu.org/licenses/>. */
#include "config.h"
#define INCLUDE_ALGORITHM
#define INCLUDE_MAP
#define INCLUDE_STRING
#define INCLUDE_VECTOR
#include "system.h"
#include "coretypes.h"
#include "diagnostic.h"
#include "diagnostics/paths.h"
#include "diagnostics/state-graphs.h"
/* Disable warnings about missing quoting in GCC diagnostics for the print
calls below. */
#if __GNUC__ >= 10
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-diag"
#endif
using namespace diagnostics;
using namespace diagnostics::paths;
/* class diagnostics::paths::event. */
/* struct event::meaning. */
void
event::meaning::dump_to_pp (pretty_printer *pp) const
{
bool need_comma = false;
pp_character (pp, '{');
if (const char *verb_str = maybe_get_verb_str (m_verb))
{
pp_printf (pp, "verb: %qs", verb_str);
need_comma = true;
}
if (const char *noun_str = maybe_get_noun_str (m_noun))
{
if (need_comma)
pp_string (pp, ", ");
pp_printf (pp, "noun: %qs", noun_str);
need_comma = true;
}
if (const char *property_str = maybe_get_property_str (m_property))
{
if (need_comma)
pp_string (pp, ", ");
pp_printf (pp, "property: %qs", property_str);
need_comma = true;
}
pp_character (pp, '}');
}
/* Get a string (or nullptr) for V suitable for use within a SARIF
threadFlowLocation "kinds" property (SARIF v2.1.0 section 3.38.8). */
const char *
event::meaning::maybe_get_verb_str (enum verb v)
{
switch (v)
{
default:
gcc_unreachable ();
case verb::unknown:
return nullptr;
case verb::acquire:
return "acquire";
case verb::release:
return "release";
case verb::enter:
return "enter";
case verb::exit:
return "exit";
case verb::call:
return "call";
case verb::return_:
return "return";
case verb::branch:
return "branch";
case verb::danger:
return "danger";
}
}
/* Get a string (or nullptr) for N suitable for use within a SARIF
threadFlowLocation "kinds" property (SARIF v2.1.0 section 3.38.8). */
const char *
event::meaning::maybe_get_noun_str (enum noun n)
{
switch (n)
{
default:
gcc_unreachable ();
case noun::unknown:
return nullptr;
case noun::taint:
return "taint";
case noun::sensitive:
return "sensitive";
case noun::function:
return "function";
case noun::lock:
return "lock";
case noun::memory:
return "memory";
case noun::resource:
return "resource";
}
}
/* Get a string (or nullptr) for P suitable for use within a SARIF
threadFlowLocation "kinds" property (SARIF v2.1.0 section 3.38.8). */
const char *
event::meaning::maybe_get_property_str (enum property p)
{
switch (p)
{
default:
gcc_unreachable ();
case property::unknown:
return nullptr;
case property::true_:
return "true";
case property::false_:
return "false";
}
}
/* Generate a label_text containing the description of this event
(for debugging/logging purposes). */
label_text
event::get_desc (pretty_printer &ref_pp) const
{
auto pp = ref_pp.clone ();
pp_show_color (pp.get ()) = false;
print_desc (*pp.get ());
return label_text::take (xstrdup (pp_formatted_text (pp.get ())));
}
// Base implementation of event::maybe_make_diagnostic_state_graph
std::unique_ptr<digraphs::digraph>
event::maybe_make_diagnostic_state_graph (bool) const
{
// Don't attempt to make a state graph:
return nullptr;
}
/* class diagnostics::paths::path. */
/* Subroutine of path::interprocedural_p.
Look for the first event in this path that is within a function
i.e. has a non-null logical location for which function_p is true.
If found, write its index to *OUT_IDX and return true.
Otherwise return false. */
bool
path::get_first_event_in_a_function (unsigned *out_idx) const
{
const unsigned num = num_events ();
for (unsigned i = 0; i < num; i++)
{
const event &event = get_event (i);
if (logical_locations::key logical_loc = event.get_logical_location ())
if (m_logical_loc_mgr.function_p (logical_loc))
{
*out_idx = i;
return true;
}
}
return false;
}
/* Return true if the events in this path involve more than one
function, or false if it is purely intraprocedural. */
bool
path::interprocedural_p () const
{
/* Ignore leading events that are outside of any function. */
unsigned first_fn_event_idx;
if (!get_first_event_in_a_function (&first_fn_event_idx))
return false;
const event &first_fn_event = get_event (first_fn_event_idx);
int first_fn_stack_depth = first_fn_event.get_stack_depth ();
const unsigned num = num_events ();
for (unsigned i = first_fn_event_idx + 1; i < num; i++)
{
if (!same_function_p (first_fn_event_idx, i))
return true;
if (get_event (i).get_stack_depth () != first_fn_stack_depth)
return true;
}
return false;
}
/* Print PATH by emitting a dummy "note" associated with it. */
DEBUG_FUNCTION
void debug (path *p)
{
rich_location richloc (line_table, UNKNOWN_LOCATION);
richloc.set_path (p);
inform (&richloc, "debug path");
}
#if __GNUC__ >= 10
# pragma GCC diagnostic pop
#endif