]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
analyzer: add event kinds for special control flow [PR122544]
authorDavid Malcolm <dmalcolm@redhat.com>
Tue, 4 Nov 2025 02:42:59 +0000 (21:42 -0500)
committerDavid Malcolm <dmalcolm@redhat.com>
Tue, 4 Nov 2025 02:42:59 +0000 (21:42 -0500)
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 <dmalcolm@redhat.com>
gcc/analyzer/checker-event.cc
gcc/analyzer/checker-event.h
gcc/diagnostics/paths.cc
gcc/diagnostics/paths.h
gcc/testsuite/g++.dg/analyzer/exception-path-1-sarif.py [new file with mode: 0644]
gcc/testsuite/g++.dg/analyzer/exception-path-1.C
gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2-sarif.py [new file with mode: 0644]
gcc/testsuite/g++.dg/analyzer/exception-path-unwind-multiple-2.C
gcc/testsuite/gcc.dg/analyzer/setjmp-3-sarif.py [new file with mode: 0644]
gcc/testsuite/gcc.dg/analyzer/setjmp-3.c

index 790ebc714380060d416aa015afe359a673b46770..3e54c2a408c0b17bbc3ff802362730f8ed41f4c6 100644 (file)
@@ -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
index 909e3889e28aa2c4cf31fdc28876d54b6d2a461a..fc51be10c956e8033b080280d842b5086fc0a7d7 100644 (file)
@@ -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;
index 824b810cb3bf517ffd6788e179cc35a964a8a372..8e29dae2cd71e7a45021484417b0b7d3ad102273 100644 (file)
@@ -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";
     }
 }
 
index d30c4203ec5e9cba3063ebaa59d849b4be7e3ced..f7dff8d7c69131e48164d4aa851944a724df6869 100644 (file)
@@ -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 (file)
index 0000000..8958d96
--- /dev/null
@@ -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"]
index 486ca19389231000b79b8965700c3d3cd74d127e..d923d62121bf0e31b7dda46ff1cd05737747f19e 100644 (file)
@@ -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 (file)
index 0000000..b817a64
--- /dev/null
@@ -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"]
index 2608f17079d358bbd65dd925dc5b11b54b58a047..aa1ff89ffb510480335a5d6e223dbd3a2a40f29e 100644 (file)
@@ -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 (file)
index 0000000..922d338
--- /dev/null
@@ -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"]
index 3e4f870e5e8f7c744f35863fdb2a5cbf169eaa66..a19ce8412b8f7788e8ca194c560f0446d6d727b9 100644 (file)
@@ -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" } } */