This patch adds support to sarif-replay for "nestingLevel" from "P3358R0 SARIF for Structured Diagnostics" https://wg21.link/P3358R0
Doing so revealed a bug where libgdiagnostics was always creating new location_t values (and thus also diagnostic_physical_location instances), rather than reusing existing location_t values, leading to excess source printing. The patch also fixes this bug, adding a new flag to libgdiagnostics for debugging physical locations, and exposing this in sarif-replay via a new "-fdebug-physical-locations" maintainer option. Finally, the patch adds test coverage for the HTML sink's output of nested diagnostics (both from a GCC plugin, and from sarif-replay). Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu. Pushed to trunk as r16-2766-g7969e4859ed007. gcc/ChangeLog: PR diagnostics/116253 * diagnostics/context.cc (context::set_nesting_level): New. * diagnostics/context.h (context::set_nesting_level): New decl. * doc/libgdiagnostics/topics/compatibility.rst (LIBGDIAGNOSTICS_ABI_5): New. * doc/libgdiagnostics/topics/physical-locations.rst (diagnostic_manager_set_debug_physical_locations): New. * libgdiagnostics++.h (manager::set_debug_physical_locations): New. * libgdiagnostics-private.h (private_diagnostic_set_nesting_level): New decl. * libgdiagnostics.cc (diagnostic_manager::diagnostic_manager): Initialize m_debug_physical_locations. (diagnostic_manager::new_location_from_file_and_line): Add debug printing. (diagnostic_manager::new_location_from_file_line_column): Likewise. (diagnostic_manager::new_location_from_range): Likewise. (diagnostic_manager::set_debug_physical_locations): New. (diagnostic_manager::ensure_linemap_for_file_and_line): Avoid redundant calls to linemap_add. (diagnostic_manager::new_location): Add debug printing. (diagnostic_manager::m_debug_physical_locations): New field. (diagnostic::diagnostic): Initialize m_nesting_level. (diagnostic::get_nesting_level): New accessor. (diagnostic::set_nesting_level): New. (diagnostic::m_nesting_level): New field. (diagnostic_manager::emit_va): Set and reset the nesting level of the context from that of the diagnostic. (diagnostic_manager_set_debug_physical_locations): New. (private_diagnostic_set_nesting_level): New. * libgdiagnostics.h (diagnostic_manager_set_debug_physical_locations): New decl. * libgdiagnostics.map (LIBGDIAGNOSTICS_ABI_5): New. * libsarifreplay.cc (sarif_replayer::handle_result_obj): Support the "nestingLevel" property. * libsarifreplay.h (replay_options::m_debug_physical_locations): New field. * sarif-replay.cc: Add -fdebug-physical-locations. gcc/testsuite/ChangeLog: PR diagnostics/116253 * gcc.dg/plugin/diagnostic-test-nesting-html.c: New test. * gcc.dg/plugin/diagnostic-test-nesting-html.py: New test script. * gcc.dg/plugin/plugin.exp: Add it. * libgdiagnostics.dg/test-multiple-lines.c: Update expected output to show fix-it hint. * sarif-replay.dg/2.1.0-valid/nested-diagnostics-1.sarif: New test. --- gcc/diagnostics/context.cc | 6 + gcc/diagnostics/context.h | 1 + .../libgdiagnostics/topics/compatibility.rst | 9 + .../topics/physical-locations.rst | 16 ++ gcc/libgdiagnostics++.h | 10 ++ gcc/libgdiagnostics-private.h | 7 + gcc/libgdiagnostics.cc | 77 +++++++- gcc/libgdiagnostics.h | 10 ++ gcc/libgdiagnostics.map | 8 + gcc/libsarifreplay.cc | 12 ++ gcc/libsarifreplay.h | 1 + gcc/sarif-replay.cc | 13 +- .../plugin/diagnostic-test-nesting-html.c | 13 ++ .../plugin/diagnostic-test-nesting-html.py | 69 ++++++++ gcc/testsuite/gcc.dg/plugin/plugin.exp | 1 + .../libgdiagnostics.dg/test-multiple-lines.c | 1 + .../2.1.0-valid/nested-diagnostics-1.sarif | 164 ++++++++++++++++++ 17 files changed, 408 insertions(+), 10 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-html.c create mode 100644 gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-html.py create mode 100644 gcc/testsuite/sarif-replay.dg/2.1.0-valid/nested-diagnostics-1.sarif diff --git a/gcc/diagnostics/context.cc b/gcc/diagnostics/context.cc index 4d33f97017f7..a1441ca5e73a 100644 --- a/gcc/diagnostics/context.cc +++ b/gcc/diagnostics/context.cc @@ -1681,6 +1681,12 @@ context::pop_nesting_level () inhibit_notes_in_group (/*inhibit=*/false); } +void +context::set_nesting_level (int new_level) +{ + m_diagnostic_groups.m_diagnostic_nesting_level = new_level; +} + void sink::dump (FILE *out, int indent) const { diff --git a/gcc/diagnostics/context.h b/gcc/diagnostics/context.h index a5f1a41967b4..b6ec85cc26ee 100644 --- a/gcc/diagnostics/context.h +++ b/gcc/diagnostics/context.h @@ -301,6 +301,7 @@ public: void push_nesting_level (); void pop_nesting_level (); + void set_nesting_level (int new_level); bool warning_enabled_at (location_t loc, option_id opt_id); diff --git a/gcc/doc/libgdiagnostics/topics/compatibility.rst b/gcc/doc/libgdiagnostics/topics/compatibility.rst index 0ca41a330095..ccf123650f98 100644 --- a/gcc/doc/libgdiagnostics/topics/compatibility.rst +++ b/gcc/doc/libgdiagnostics/topics/compatibility.rst @@ -262,3 +262,12 @@ working with :type:`diagnostic_message_buffer`. * :func:`diagnostic_add_location_with_label_via_msg_buf` * :func:`diagnostic_execution_path_add_event_via_msg_buf` + +.. _LIBGDIAGNOSTICS_ABI_5: + +``LIBGDIAGNOSTICS_ABI_5`` +------------------------- + +``LIBGDIAGNOSTICS_ABI_5`` covers the addition of this function: + + * :func:`diagnostic_manager_set_debug_physical_locations` diff --git a/gcc/doc/libgdiagnostics/topics/physical-locations.rst b/gcc/doc/libgdiagnostics/topics/physical-locations.rst index be8e7ebc5d1f..fcd81a02741c 100644 --- a/gcc/doc/libgdiagnostics/topics/physical-locations.rst +++ b/gcc/doc/libgdiagnostics/topics/physical-locations.rst @@ -304,3 +304,19 @@ This diagnostic has three locations .. code-block:: c #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_message_buffer + +.. function:: void diagnostic_manager_set_debug_physical_locations (diagnostic_manager *mgr, \ + int value) + + Calling ``diagnostic_manager_set_debug_physical_locations (mgr, 1);`` + will lead to debugging information being printed to ``stderr`` when + creating :type:`diagnostic_physical_location` instances. + + The precise format of these messages is subject to change. + + This function was added in :ref:`LIBGDIAGNOSTICS_ABI_5`; you can + test for its presence using + + .. code-block:: c + + #ifdef LIBDIAGNOSTICS_HAVE_diagnostic_manager_set_debug_physical_locations diff --git a/gcc/libgdiagnostics++.h b/gcc/libgdiagnostics++.h index c955d56db5b3..fc20398f90c2 100644 --- a/gcc/libgdiagnostics++.h +++ b/gcc/libgdiagnostics++.h @@ -477,6 +477,9 @@ public: void take_global_graph (graph g); + void + set_debug_physical_locations (bool value); + diagnostic_manager *m_inner; bool m_owned; }; @@ -926,6 +929,13 @@ manager::take_global_graph (graph g) g.m_owned = false; } +inline void +manager::set_debug_physical_locations (bool value) +{ + diagnostic_manager_set_debug_physical_locations (m_inner, + value ? 1 : 0); +} + // class graph inline void diff --git a/gcc/libgdiagnostics-private.h b/gcc/libgdiagnostics-private.h index 0e90f8720079..4186c67e67e3 100644 --- a/gcc/libgdiagnostics-private.h +++ b/gcc/libgdiagnostics-private.h @@ -58,6 +58,13 @@ private_diagnostic_execution_path_add_event_3 (diagnostic_execution_path *path, LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (5) LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (6); +/* Entrypoint added in LIBGDIAGNOSTICS_ABI_5. */ + +extern void +private_diagnostic_set_nesting_level (diagnostic *diag, + int nesting_level) + LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1); + } // extern "C" #endif /* LIBGDIAGNOSTICS_PRIVATE_H */ diff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc index 7351d33641bc..784e281a4902 100644 --- a/gcc/libgdiagnostics.cc +++ b/gcc/libgdiagnostics.cc @@ -639,7 +639,8 @@ struct diagnostic_manager public: diagnostic_manager () : m_current_diag (nullptr), - m_prev_diag_logical_loc (nullptr) + m_prev_diag_logical_loc (nullptr), + m_debug_physical_locations (false) { linemap_init (&m_line_table, BUILTINS_LOCATION); m_line_table.m_reallocator = xrealloc; @@ -747,6 +748,9 @@ public: new_location_from_file_and_line (const diagnostic_file *file, diagnostic_line_num_t line_num) { + if (m_debug_physical_locations) + fprintf (stderr, "new_location_from_file_and_line (%s, %i)", + file->get_name (), line_num); ensure_linemap_for_file_and_line (file, line_num); location_t loc = linemap_position_for_column (&m_line_table, 0); return new_location (loc); @@ -757,6 +761,9 @@ public: diagnostic_line_num_t line_num, diagnostic_column_num_t column_num) { + if (m_debug_physical_locations) + fprintf (stderr, "new_location_from_file_line_column (%s, %i, %i)", + file->get_name (), line_num, column_num); ensure_linemap_for_file_and_line (file, line_num); location_t loc = linemap_position_for_column (&m_line_table, column_num); return new_location (loc); @@ -767,12 +774,23 @@ public: const diagnostic_physical_location *loc_start, const diagnostic_physical_location *loc_end) { + if (m_debug_physical_locations) + fprintf (stderr, "new_location_from_range (%p, %p, %p)", + (const void *)loc_caret, + (const void *)loc_start, + (const void *)loc_end); return new_location (m_line_table.make_location (as_location_t (loc_caret), as_location_t (loc_start), as_location_t (loc_end))); } + void + set_debug_physical_locations (bool value) + { + m_debug_physical_locations = value; + } + const diagnostic_logical_location * new_logical_location (enum diagnostic_logical_location_kind_t kind, const diagnostic_logical_location *parent, @@ -874,11 +892,17 @@ private: linemap_add (&m_line_table, LC_ENTER, false, file->get_name (), 0); else { - line_map *map - = const_cast<line_map *> - (linemap_add (&m_line_table, LC_RENAME_VERBATIM, false, - file->get_name (), 0)); - ((line_map_ordinary *)map)->included_from = UNKNOWN_LOCATION; + line_map_ordinary *last_map + = LINEMAPS_LAST_ORDINARY_MAP (&m_line_table); + if (last_map->to_file != file->get_name () + || linenum < last_map->to_line) + { + line_map *map + = const_cast<line_map *> + (linemap_add (&m_line_table, LC_RENAME_VERBATIM, false, + file->get_name (), 0)); + ((line_map_ordinary *)map)->included_from = UNKNOWN_LOCATION; + } } linemap_line_start (&m_line_table, linenum, 100); } @@ -888,11 +912,19 @@ private: { if (loc == UNKNOWN_LOCATION) return nullptr; + if (m_debug_physical_locations) + fprintf (stderr, ": new_location (%lx)", loc); if (diagnostic_physical_location **slot = m_location_t_map.get (loc)) - return *slot; + { + if (m_debug_physical_locations) + fprintf (stderr, ": cache hit: %p\n", (const void *)*slot); + return *slot; + } diagnostic_physical_location *phys_loc = new diagnostic_physical_location (this, loc); m_location_t_map.put (loc, phys_loc); + if (m_debug_physical_locations) + fprintf (stderr, ": cache miss: %p\n", (const void *)phys_loc); return phys_loc; } @@ -909,6 +941,7 @@ private: const diagnostic *m_current_diag; const diagnostic_logical_location *m_prev_diag_logical_loc; std::unique_ptr<diagnostics::changes::change_set> m_change_set; + bool m_debug_physical_locations; }; class impl_rich_location : public rich_location @@ -1221,7 +1254,8 @@ public: m_level (level), m_rich_loc (diag_mgr.get_line_table ()), m_logical_loc (nullptr), - m_path (nullptr) + m_path (nullptr), + m_nesting_level (0) { m_metadata.set_lazy_digraphs (&m_graphs); } @@ -1325,6 +1359,9 @@ public: return m_graphs; } + int get_nesting_level () const { return m_nesting_level; } + void set_nesting_level (int value) { m_nesting_level = value; } + private: diagnostic_manager &m_diag_mgr; enum diagnostic_level m_level; @@ -1335,6 +1372,7 @@ private: std::vector<std::unique_ptr<range_label>> m_labels; std::vector<std::unique_ptr<impl_rule>> m_rules; std::unique_ptr<diagnostic_execution_path> m_path; + int m_nesting_level; }; static enum diagnostics::kind @@ -1624,8 +1662,9 @@ GCC_DIAGNOSTIC_PUSH_IGNORED(-Wsuggest-attribute=format) GCC_DIAGNOSTIC_POP info.m_metadata = diag.get_metadata (); info.m_x_data = &diag; + m_dc.set_nesting_level (diag.get_nesting_level ()); diagnostic_report_diagnostic (&m_dc, &info); - + m_dc.set_nesting_level (0); m_dc.end_group (); } @@ -2972,3 +3011,23 @@ private_diagnostic_execution_path_add_event_3 (diagnostic_execution_path *path, return as_diagnostic_event_id (result); } + +/* Public entrypoint. */ + +void +diagnostic_manager_set_debug_physical_locations (diagnostic_manager *mgr, + int value) +{ + FAIL_IF_NULL (mgr); + mgr->set_debug_physical_locations (value); +} + +/* Private entrypoint. */ + +void +private_diagnostic_set_nesting_level (diagnostic *diag, + int nesting_level) +{ + FAIL_IF_NULL (diag); + diag->set_nesting_level (nesting_level); +} diff --git a/gcc/libgdiagnostics.h b/gcc/libgdiagnostics.h index c202feb1cae3..0ae56f05ea6d 100644 --- a/gcc/libgdiagnostics.h +++ b/gcc/libgdiagnostics.h @@ -1115,6 +1115,16 @@ diagnostic_node_set_label_via_msg_buf (diagnostic_node *node, LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (1) LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL (2); +/* If non-zero, print debugging information to stderr when + creating diagnostic_physical_location instances. + + Added in LIBGDIAGNOSTICS_ABI_5. */ +#define LIBDIAGNOSTICS_HAVE_diagnostic_manager_set_debug_physical_locations + +extern void +diagnostic_manager_set_debug_physical_locations (diagnostic_manager *mgr, + int value); + /* DEFERRED: - thread-safety - plural forms diff --git a/gcc/libgdiagnostics.map b/gcc/libgdiagnostics.map index 91f3951a35b4..0400ca7268a7 100644 --- a/gcc/libgdiagnostics.map +++ b/gcc/libgdiagnostics.map @@ -141,3 +141,11 @@ LIBGDIAGNOSTICS_ABI_4 { # Private hook used by sarif-replay private_diagnostic_execution_path_add_event_3; } LIBGDIAGNOSTICS_ABI_3; + +LIBGDIAGNOSTICS_ABI_5 { + global: + diagnostic_manager_set_debug_physical_locations; + + # Private hook used by sarif-replay + private_diagnostic_set_nesting_level; +} LIBGDIAGNOSTICS_ABI_4; diff --git a/gcc/libsarifreplay.cc b/gcc/libsarifreplay.cc index 1e4a74f71b55..e8fc6d0d170c 100644 --- a/gcc/libsarifreplay.cc +++ b/gcc/libsarifreplay.cc @@ -1408,10 +1408,22 @@ sarif_replayer::handle_result_obj (const json::object &result_obj, rule_obj)); if (!msg_buf.m_inner) return status::err_invalid_sarif; + auto note (m_output_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_NOTE)); note.set_location (physical_loc); note.set_logical_location (logical_loc); add_any_annotations (note, annotations); + + /* Look for "nestingLevel" property, as per + "P3358R0 SARIF for Structured Diagnostics" + https://wg21.link/P3358R0 */ + if (auto nesting_level + = maybe_get_property_bag_value<json::integer_number> + (*location_obj, + "nestingLevel")) + private_diagnostic_set_nesting_level (note.m_inner, + nesting_level->get ()); + notes.push_back ({std::move (note), std::move (msg_buf)}); } else diff --git a/gcc/libsarifreplay.h b/gcc/libsarifreplay.h index fb66014086a0..313a905754e0 100644 --- a/gcc/libsarifreplay.h +++ b/gcc/libsarifreplay.h @@ -31,6 +31,7 @@ struct replay_options bool m_echo_file; bool m_json_comments; bool m_verbose; + bool m_debug_physical_locations; enum diagnostic_colorize m_diagnostics_colorize; }; diff --git a/gcc/sarif-replay.cc b/gcc/sarif-replay.cc index a96c97bd92ba..c740c29b2a32 100644 --- a/gcc/sarif-replay.cc +++ b/gcc/sarif-replay.cc @@ -93,6 +93,11 @@ static const char *const usage_msg = ( "\n" " --usage\n" " Print this message and exit.\n" +"\n" +"Options for maintainers:\n" +"\n" +" -fdebug-physical-locations\n" +" Dump debugging information about physical locations to stderr.\n" "\n"); static void @@ -182,7 +187,11 @@ parse_options (int argc, char **argv, print_version (); exit (0); } - + else if (strcmp (option, "-fdebug-physical-locations") == 0) + { + opts.m_replay_opts.m_debug_physical_locations = true; + handled = true; + } if (!handled) { if (option[0] == '-') @@ -245,6 +254,8 @@ main (int argc, char **argv) note.finish ("about to replay %qs...", filename); } libgdiagnostics::manager playback_mgr; + playback_mgr.set_debug_physical_locations + (opts.m_replay_opts.m_debug_physical_locations); playback_mgr.add_text_sink (stderr, opts.m_replay_opts.m_diagnostics_colorize); for (auto spec : opts.m_extra_output_specs) diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-html.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-html.c new file mode 100644 index 000000000000..8ff7b355bff7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-html.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-add-output=experimental-html:javascript=no" } */ + +extern void foo (void); + +void test_nesting (void) +{ + foo (); /* { dg-error "top-level error" } */ +} + +/* Use a Python script to verify various properties about the generated + .html file: + { dg-final { run-html-pytest diagnostic-test-nesting-html.c "diagnostic-test-nesting-html.py" } } */ diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-html.py b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-html.py new file mode 100644 index 000000000000..3899ba530d98 --- /dev/null +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-nesting-html.py @@ -0,0 +1,69 @@ +# Verify that nesting works in HTML output. + +from htmltest import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def html_tree(): + return html_tree_from_env() + +def test_nesting(html_tree): + root = html_tree.getroot () + assert root.tag == make_tag('html') + + body = root.find('xhtml:body', ns) + assert body is not None + + diag_list = body.find('xhtml:div', ns) + assert diag_list is not None + assert diag_list.attrib['class'] == 'gcc-diagnostic-list' + + diag = diag_list.find('xhtml:div', ns) + assert diag is not None + + message = diag.find("./xhtml:div[@class='gcc-message']", ns) + assert message.attrib['id'] == 'gcc-diag-0-message' + + assert message[0].tag == make_tag('strong') + assert message[0].tail == ' top-level error' + + # We expect 12 messages, with the given IDs and text: + for i in range(12): + child = diag.find(".//xhtml:div[@id='gcc-diag-%i']" % (i + 1), + ns) + assert child is not None + + message = child.find("./xhtml:div[@class='gcc-message']", ns) + assert message.attrib['id'] == 'gcc-diag-%i-message' % (i + 1) + + if i % 4 == 0: + assert message.text == 'child %i' % (i / 4) + else: + assert message.text == 'grandchild %i %i' % ((i / 4), (i % 4) - 1) + + # We expect the messages to be organized into nested <ul> with + # "nesting-level" set, all below a <ul> + child_ul = diag.find("./xhtml:ul[@nesting-level='1']", ns) + assert child_ul is not None + msg_id = 1 + for i in range(3): + child_li = child_ul.find("./xhtml:li[@nesting-level='1'][%i]" % (i + 1), ns) + assert child_li is not None + child = child_li.find("./xhtml:div[@id='gcc-diag-%i']" % msg_id, ns) + assert child is not None + message = child.find("./xhtml:div[@class='gcc-message']", ns) + assert message.attrib['id'] == 'gcc-diag-%i-message' % msg_id + assert message.text == 'child %i' % i + msg_id += 1 + grandchild_ul = child_ul.find("./xhtml:ul[@nesting-level='2'][%i]" % (i + 1), ns) + assert grandchild_ul is not None + for j in range(3): + grandchild_li = grandchild_ul.find("./xhtml:li[@nesting-level='2'][%i]" % (j + 1), ns) + assert grandchild_li is not None + grandchild = grandchild_li.find("./xhtml:div[@id='gcc-diag-%i']" % msg_id, ns) + assert grandchild is not None + message = grandchild.find("./xhtml:div[@class='gcc-message']", ns) + assert message.attrib['id'] == 'gcc-diag-%i-message' % msg_id + assert message.text == 'grandchild %i %i' % (i, j) + msg_id += 1 diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp index ce25c0ab3abd..3bb6063c3a9e 100644 --- a/gcc/testsuite/gcc.dg/plugin/plugin.exp +++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp @@ -116,6 +116,7 @@ set plugin_test_list [list \ diagnostic-test-nesting-text-indented.c \ diagnostic-test-nesting-text-indented-show-levels.c \ diagnostic-test-nesting-text-indented-unicode.c \ + diagnostic-test-nesting-html.c \ diagnostic-test-nesting-sarif.c } \ { diagnostic_plugin_test_paths.cc \ diagnostic-test-paths-1.c \ diff --git a/gcc/testsuite/libgdiagnostics.dg/test-multiple-lines.c b/gcc/testsuite/libgdiagnostics.dg/test-multiple-lines.c index e76111093427..39af8105e6bd 100644 --- a/gcc/testsuite/libgdiagnostics.dg/test-multiple-lines.c +++ b/gcc/testsuite/libgdiagnostics.dg/test-multiple-lines.c @@ -66,6 +66,7 @@ main () | ~~~~~ 23 | "bar" | ~~~~~^ + | , 24 | "baz"}; | ~~~~~ { dg-end-multiline-output "" } */ diff --git a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/nested-diagnostics-1.sarif b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/nested-diagnostics-1.sarif new file mode 100644 index 000000000000..b924012eaf71 --- /dev/null +++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/nested-diagnostics-1.sarif @@ -0,0 +1,164 @@ +/* Test a replay of a .sarif file generated from GCC testsuite. + + The dg directives were stripped out from the generated .sarif + to avoid confusing DejaGnu for this test. */ +/* { dg-additional-options "-fdiagnostics-add-output=sarif:file=nested-diagnostics-1.roundtrip.sarif" } */ +/* { dg-additional-options "-fdiagnostics-add-output=experimental-html:file=nested-diagnostics-1.sarif.html,javascript=no" } */ + +{"$schema": "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [{"tool": {"driver": {"name": "GNU C23", + "fullName": "GNU C23 (GCC) version 16.0.0 20250723 (experimental) (x86_64-pc-linux-gnu)", + "version": "16.0.0 20250723 (experimental)", + "informationUri": "https://gcc.gnu.org/gcc-16/", + "rules": []}, + "extensions": [{"name": "diagnostic_plugin_test_nesting", + "fullName": "./diagnostic_plugin_test_nesting.so"}]}, + "invocations": [{"executionSuccessful": false, + "toolExecutionNotifications": []}], + "artifacts": [{"location": {"uri": "diagnostic-test-nesting-sarif.c"}, + "sourceLanguage": "c", + "contents": {"text": "\n\nextern void foo (void);\n\nvoid test_nesting (void)\n{\n foo ();\n}\n"}, + "roles": ["analysisTarget"]}], + "results": [{"ruleId": "error", + "level": "error", + "message": {"text": "top-level error"}, + "locations": [{"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "logicalLocations": [{"index": 0, + "fullyQualifiedName": "test_nesting"}]}], + "relatedLocations": [{"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "message": {"text": "child 0"}, + "properties": {"nestingLevel": 1}}, + {"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "message": {"text": "grandchild 0 0"}, + "properties": {"nestingLevel": 2}}, + {"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "message": {"text": "grandchild 0 1"}, + "properties": {"nestingLevel": 2}}, + {"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "message": {"text": "grandchild 0 2"}, + "properties": {"nestingLevel": 2}}, + {"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "message": {"text": "child 1"}, + "properties": {"nestingLevel": 1}}, + {"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "message": {"text": "grandchild 1 0"}, + "properties": {"nestingLevel": 2}}, + {"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "message": {"text": "grandchild 1 1"}, + "properties": {"nestingLevel": 2}}, + {"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "message": {"text": "grandchild 1 2"}, + "properties": {"nestingLevel": 2}}, + {"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "message": {"text": "child 2"}, + "properties": {"nestingLevel": 1}}, + {"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "message": {"text": "grandchild 2 0"}, + "properties": {"nestingLevel": 2}}, + {"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "message": {"text": "grandchild 2 1"}, + "properties": {"nestingLevel": 2}}, + {"physicalLocation": {"artifactLocation": {"uri": "diagnostic-test-nesting-sarif.c"}, + "region": {"startLine": 8, + "startColumn": 3, + "endColumn": 9}, + "contextRegion": {"startLine": 8, + "snippet": {"text": " foo ();\n"}}}, + "message": {"text": "grandchild 2 2"}, + "properties": {"nestingLevel": 2}}]}], + "logicalLocations": [{"name": "test_nesting", + "fullyQualifiedName": "test_nesting", + "decoratedName": "test_nesting", + "kind": "function", + "index": 0}]}]} + +/* For now, we don't have a way of enabling showing the nesting + on the default text output. However we do test the nesting + in the SARIF and HTML outputs below. */ +/* { dg-begin-multiline-output "" } +In function 'test_nesting': +diagnostic-test-nesting-sarif.c:8:3: error: top-level error + 8 | } + | ^ +diagnostic-test-nesting-sarif.c:8:3: note: child 0 +diagnostic-test-nesting-sarif.c:8:3: note: grandchild 0 0 +diagnostic-test-nesting-sarif.c:8:3: note: grandchild 0 1 +diagnostic-test-nesting-sarif.c:8:3: note: grandchild 0 2 +diagnostic-test-nesting-sarif.c:8:3: note: child 1 +diagnostic-test-nesting-sarif.c:8:3: note: grandchild 1 0 +diagnostic-test-nesting-sarif.c:8:3: note: grandchild 1 1 +diagnostic-test-nesting-sarif.c:8:3: note: grandchild 1 2 +diagnostic-test-nesting-sarif.c:8:3: note: child 2 +diagnostic-test-nesting-sarif.c:8:3: note: grandchild 2 0 +diagnostic-test-nesting-sarif.c:8:3: note: grandchild 2 1 +diagnostic-test-nesting-sarif.c:8:3: note: grandchild 2 2 + { dg-end-multiline-output "" } */ + +/* Use a Python script to verify various properties about the *generated* + .sarif file: + { dg-final { run-sarif-pytest nested-diagnostics-1.roundtrip "../gcc.dg/plugin/diagnostic-test-nesting-sarif.py" } } */ + +/* Use a Python script to verify various properties about the generated + .html file: + { dg-final { run-html-pytest nested-diagnostics-1.sarif "../gcc.dg/plugin/diagnostic-test-nesting-html.py" } } */ -- 2.26.3