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
{
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);
* :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`
.. 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
void
take_global_graph (graph g);
+ void
+ set_debug_physical_locations (bool value);
+
diagnostic_manager *m_inner;
bool m_owned;
};
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
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 */
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;
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);
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);
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,
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);
}
{
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;
}
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
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);
}
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;
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
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 ();
}
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);
+}
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
# 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;
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
bool m_echo_file;
bool m_json_comments;
bool m_verbose;
+ bool m_debug_physical_locations;
enum diagnostic_colorize m_diagnostics_colorize;
};
"\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
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] == '-')
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)
--- /dev/null
+/* { 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" } } */
--- /dev/null
+# 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
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 \
| ~~~~~
23 | "bar"
| ~~~~~^
+ | ,
24 | "baz"};
| ~~~~~
{ dg-end-multiline-output "" } */
--- /dev/null
+/* 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" } } */