From: David Malcolm Date: Tue, 4 Nov 2025 02:42:59 +0000 (-0500) Subject: analyzer: add event kinds for special control flow [PR122544] X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0a2c4b6dd2019d144df49227fce950e10ae62b09;p=thirdparty%2Fgcc.git analyzer: add event kinds for special control flow [PR122544] The SARIF 3.38.8 "kinds" property has some verbs for expressing control flow, but is missing some of the awkward special cases. The spec says "If none of these values are appropriate, a SARIF producer MAY use any value." This patch adds the following new values: * "throw" for throwing an exception * "catch" for catching an exception * "unwind" for unwinding stack frame(s) during exception-handling * "setjmp" for calls to setjmp * "longjmp" for calls to longjmp that rewind the program counter/stack to the location of a previous setjmp call gcc/analyzer/ChangeLog: PR analyzer/122544 * checker-event.cc (catch_cfg_edge_event::get_meaning): New. (setjmp_event::get_meaning): New. (rewind_event::get_meaning): New. (throw_event::get_meaning): New. (unwind_event::get_meaning): New. * checker-event.h (catch_cfg_edge_event::get_meaning): New decl. (setjmp_event::get_meaning): New decl. (rewind_event::get_meaning): New decl. (throw_event::get_meaning): New decl. (unwind_event::get_meaning): New decl. gcc/ChangeLog: PR analyzer/122544 * diagnostics/paths.cc (event::meaning::maybe_get_verb_str): Handle the new verbs. * diagnostics/paths.h (event::meaning::verb): Add new values for special control flow operations. (event::meaning::meaning): Add ctor taking just a verb. gcc/testsuite/ChangeLog: PR analyzer/122544 * g++.dg/analyzer/exception-path-1-sarif.py: New test script. * g++.dg/analyzer/exception-path-1.C: Add SARIF output, and use the above to check it. * g++.dg/analyzer/exception-path-unwind-multiple-2-sarif.py: New test script. * g++.dg/analyzer/exception-path-unwind-multiple-2.C: Add SARIF output, and use the above to check it. * gcc.dg/analyzer/setjmp-3-sarif.py: New test script. * gcc.dg/analyzer/setjmp-3.c: Add SARIF output, and use the above to check it. Signed-off-by: David Malcolm --- diff --git a/gcc/analyzer/checker-event.cc b/gcc/analyzer/checker-event.cc index 790ebc71438..3e54c2a408c 100644 --- a/gcc/analyzer/checker-event.cc +++ b/gcc/analyzer/checker-event.cc @@ -833,6 +833,14 @@ start_cfg_edge_event::should_print_expr_p (tree expr) return false; } +/* class catch_cfg_edge_event : public cfg_edge_event. */ + +diagnostics::paths::event::meaning +catch_cfg_edge_event::get_meaning () const +{ + return meaning (verb::catch_); +} + /* class call_event : public superedge_event. */ /* call_event's ctor. */ @@ -1034,6 +1042,12 @@ setjmp_event::print_desc (pretty_printer &pp) const get_user_facing_name (m_setjmp_call)); } +diagnostics::paths::event::meaning +setjmp_event::get_meaning () const +{ + return meaning (verb::setjmp_); +} + /* Implementation of checker_event::prepare_for_emission vfunc for setjmp_event. Record this setjmp's event ID into the path, so that rewind events can @@ -1066,6 +1080,12 @@ rewind_event::get_setjmp_caller () const return m_eedge->m_dest->get_function ()->decl; } +diagnostics::paths::event::meaning +rewind_event::get_meaning () const +{ + return meaning (verb::longjmp_); +} + /* rewind_event's ctor. */ rewind_event::rewind_event (const exploded_edge *eedge, @@ -1163,6 +1183,12 @@ rewind_to_setjmp_event::prepare_for_emission (checker_path *path, /* class throw_event : public checker_event. */ +diagnostics::paths::event::meaning +throw_event::get_meaning () const +{ + return meaning (verb::throw_); +} + /* class explicit_throw_event : public throw_event. */ void explicit_throw_event::print_desc (pretty_printer &pp) const @@ -1205,6 +1231,12 @@ unwind_event::print_desc (pretty_printer &pp) const pp_printf (&pp, "unwinding stack frame"); } +diagnostics::paths::event::meaning +unwind_event::get_meaning () const +{ + return meaning (verb::unwind_); +} + /* class warning_event : public checker_event. */ /* Implementation of diagnostics::paths::event::print_desc vfunc for diff --git a/gcc/analyzer/checker-event.h b/gcc/analyzer/checker-event.h index 909e3889e28..fc51be10c95 100644 --- a/gcc/analyzer/checker-event.h +++ b/gcc/analyzer/checker-event.h @@ -539,6 +539,8 @@ public: pp_string (&pp, "...catching exception here"); } + meaning get_meaning () const override; + private: tree m_type; }; @@ -666,6 +668,8 @@ public: void print_desc (pretty_printer &pp) const final override; + meaning get_meaning () const override; + void prepare_for_emission (checker_path *path, pending_diagnostic *pd, diagnostics::paths::event_id_t emission_id) final override; @@ -688,6 +692,8 @@ public: tree get_setjmp_caller () const; const exploded_edge *get_eedge () const { return m_eedge; } + meaning get_meaning () const override; + protected: rewind_event (const exploded_edge *eedge, enum event_kind kind, @@ -754,6 +760,8 @@ public: { } + meaning get_meaning () const override; + protected: const exploded_node *m_enode; const gcall &m_throw_call; @@ -817,6 +825,8 @@ public: { } + meaning get_meaning () const override; + void print_desc (pretty_printer &pp) const final override; int m_num_frames; diff --git a/gcc/diagnostics/paths.cc b/gcc/diagnostics/paths.cc index 824b810cb3b..8e29dae2cd7 100644 --- a/gcc/diagnostics/paths.cc +++ b/gcc/diagnostics/paths.cc @@ -97,6 +97,22 @@ event::meaning::maybe_get_verb_str (enum verb v) return "branch"; case verb::danger: return "danger"; + + /* Special control flow operations. + + These are not part of SARIF v2.1.0 section 3.38.8, but the + spec allows other values; see + https://github.com/oasis-tcs/sarif-spec/issues/735 */ + case verb::throw_: + return "throw"; + case verb::catch_: + return "catch"; + case verb::unwind_: + return "unwind"; + case verb::setjmp_: + return "setjmp"; + case verb::longjmp_: + return "longjmp"; } } diff --git a/gcc/diagnostics/paths.h b/gcc/diagnostics/paths.h index d30c4203ec5..f7dff8d7c69 100644 --- a/gcc/diagnostics/paths.h +++ b/gcc/diagnostics/paths.h @@ -96,7 +96,14 @@ class event return_, branch, - danger + danger, + + // Special control flow operations: + throw_, + catch_, + unwind_, // unwinding stack frame(s) during exception-handling + setjmp_, + longjmp_ }; enum class noun { @@ -131,6 +138,10 @@ class event m_property (property::unknown) { } + meaning (enum verb verb) + : m_verb (verb), m_noun (noun::unknown), m_property (property::unknown) + { + } meaning (enum verb verb, enum noun noun) : m_verb (verb), m_noun (noun), m_property (property::unknown) { diff --git a/gcc/testsuite/g++.dg/analyzer/exception-path-1-sarif.py b/gcc/testsuite/g++.dg/analyzer/exception-path-1-sarif.py new file mode 100644 index 00000000000..8958d96b81a --- /dev/null +++ b/gcc/testsuite/g++.dg/analyzer/exception-path-1-sarif.py @@ -0,0 +1,22 @@ +from sarif import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def sarif(): + return sarif_from_env() + +def test_kinds(sarif): + result = get_result_by_index(sarif, 0) + + assert result['level'] == 'note' + + events = result["codeFlows"][0]["threadFlows"][0]['locations'] + + # Event "(1)": "throwing exception of type 'value_error' here..." (index == 0) + assert events[0]['location']['message']['text'] == "throwing exception of type 'value_error' here..." + assert events[0]['kinds'] == ["throw"] + + # Event "(2)": "...catching exception of type 'value_error' here" (index == 1) + assert events[1]['location']['message']['text'] == "...catching exception of type 'value_error' here" + assert events[1]['kinds'] == ["catch"] diff --git a/gcc/testsuite/g++.dg/analyzer/exception-path-1.C b/gcc/testsuite/g++.dg/analyzer/exception-path-1.C index 486ca193892..d923d62121b 100644 --- a/gcc/testsuite/g++.dg/analyzer/exception-path-1.C +++ b/gcc/testsuite/g++.dg/analyzer/exception-path-1.C @@ -1,3 +1,5 @@ +/* { dg-additional-options "-fdiagnostics-add-output=sarif" } */ + /* Verify that we follow the correct paths when we know the typeinfo of an exception. */ @@ -32,3 +34,10 @@ int test () __analyzer_dump_path (); // { dg-bogus "path" } return 0; } + +/* Verify that some JSON was written to a file with the expected name. */ +/* { dg-final { verify-sarif-file } } */ + +/* Use a Python script to verify various properties about the generated + .sarif file: + { dg-final { run-sarif-pytest exception-path-1.C "exception-path-1-sarif.py" } } */ diff --git a/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2-sarif.py b/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2-sarif.py new file mode 100644 index 00000000000..b817a6434b0 --- /dev/null +++ b/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2-sarif.py @@ -0,0 +1,23 @@ +from sarif import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def sarif(): + return sarif_from_env() + +def test_kinds(sarif): + result = get_result_by_index(sarif, 0) + + assert result['level'] == 'note' + + events = result["codeFlows"][0]["threadFlows"][0]['locations'] + + assert events[-4]['location']['message']['text'] == "throwing exception of type 'value_error' here..." + assert events[-4]['kinds'] == ["throw"] + + assert events[-3]['location']['message']['text'] == "unwinding 2 stack frames" + assert events[-3]['kinds'] == ["unwind"] + + assert events[-2]['location']['message']['text'] == "...catching exception of type 'value_error' here" + assert events[-2]['kinds'] == ["catch"] diff --git a/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2.C b/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2.C index 2608f17079d..aa1ff89ffb5 100644 --- a/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2.C +++ b/gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2.C @@ -1,3 +1,5 @@ +/* { dg-additional-options "-fdiagnostics-add-output=sarif" } */ + /* Verify that we follow the correct paths when we know the typeinfo of an exception: interprocedural case where unwind multiple frame, failing to match the type. */ @@ -53,3 +55,10 @@ int outer () __analyzer_dump_path (); // { dg-bogus "path" } return 0; } + +/* Verify that some JSON was written to a file with the expected name. */ +/* { dg-final { verify-sarif-file } } */ + +/* Use a Python script to verify various properties about the generated + .sarif file: + { dg-final { run-sarif-pytest exception-path-unwind-multiple-2.C "exception-path-unwind-multiple-2-sarif.py" } } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-3-sarif.py b/gcc/testsuite/gcc.dg/analyzer/setjmp-3-sarif.py new file mode 100644 index 00000000000..922d3380ecb --- /dev/null +++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-3-sarif.py @@ -0,0 +1,23 @@ +from sarif import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def sarif(): + return sarif_from_env() + +def test_kinds(sarif): + result = get_result_by_index(sarif, 0) + + assert result['level'] == 'note' + + events = result["codeFlows"][0]["threadFlows"][0]['locations'] + + assert events[1]['location']['message']['text'] == "'setjmp' called here" + assert events[1]['kinds'] == ["setjmp"] + + assert events[6]['location']['message']['text'] == "rewinding from 'longjmp' in 'inner'..." + assert events[6]['kinds'] == ["longjmp"] + + assert events[7]['location']['message']['text'].startswith("...to 'setjmp' in 'outer'") + assert events[7]['kinds'] == ["longjmp"] diff --git a/gcc/testsuite/gcc.dg/analyzer/setjmp-3.c b/gcc/testsuite/gcc.dg/analyzer/setjmp-3.c index 3e4f870e5e8..a19ce8412b8 100644 --- a/gcc/testsuite/gcc.dg/analyzer/setjmp-3.c +++ b/gcc/testsuite/gcc.dg/analyzer/setjmp-3.c @@ -1,4 +1,6 @@ /* { dg-additional-options "-fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */ +/* { dg-additional-options "-fdiagnostics-add-output=sarif" } */ + /* { dg-enable-nn-line-numbers "" } */ /* { dg-require-effective-target indirect_jumps } */ @@ -107,3 +109,10 @@ void outer (void) | | (11) here | { dg-end-multiline-output "" } */ + +/* Verify that some JSON was written to a file with the expected name. */ +/* { dg-final { verify-sarif-file } } */ + +/* Use a Python script to verify various properties about the generated + .sarif file: + { dg-final { run-sarif-pytest setjmp-3.c "setjmp-3-sarif.py" } } */