aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorDavid Malcolm <dmalcolm@redhat.com>2025-05-12 21:45:36 -0400
committerDavid Malcolm <dmalcolm@redhat.com>2025-05-12 21:45:36 -0400
commite4ccad8faf5266248993f7896b000ccf871ded30 (patch)
treefd0ae4dffa0e0c47d20ffb806344c1a8798a2220 /gcc
parentb566167b0e413d82c4f3845d0b39e382f92bc32e (diff)
downloadgcc-e4ccad8faf5266248993f7896b000ccf871ded30.zip
gcc-e4ccad8faf5266248993f7896b000ccf871ded30.tar.gz
gcc-e4ccad8faf5266248993f7896b000ccf871ded30.tar.bz2
diagnostics: improvements to experimental-html output [PR116792]
Add barebones support for * diagnostic metadata rules * quoted source * generated patches * execution paths gcc/ChangeLog: PR other/116792 * diagnostic-format-html.cc: Include "diagnostic-format-text.h", "pretty-print-urlifier.h" and "edit-context.h". (html_builder::html_builder): Fix indentation in decl. (html_builder::make_element_for_diagnostic): Split out metadata code into make_element_for_metadata. Call make_element_for_source, make_element_for_path, and make_element_for_patch. (html_builder::make_element_for_source): New. (html_builder::make_element_for_path): New. (html_builder::make_element_for_patch): New. (html_builder::make_metadata_element): New. (html_builder::make_element_for_metadata): New. (html_output_format::get_builder): New. (selftest::test_html_diagnostic_context::get_builder): New. (selftest::test_simple_log): Update test to print a quoted string, and verify that it uses a "gcc-quoted-text" span. (selftest::test_metadata): New. (selftest::diagnostic_format_html_cc_tests): Call it. gcc/testsuite/ChangeLog: PR other/116792 * gcc.dg/html-output/missing-semicolon.py: Verify that we don't have an empty "gcc-annotated-source" and we do have a "gcc-generated-patch". * gcc.dg/plugin/diagnostic-test-metadata-html.c: New test. * gcc.dg/plugin/diagnostic-test-metadata-html.py: New test script. * gcc.dg/plugin/diagnostic-test-paths-2.c: Add "-fdiagnostics-add-output=experimental-html" to options. Add invocation of diagnostic-test-paths-2.py. * gcc.dg/plugin/diagnostic-test-paths-2.py: New test script. * gcc.dg/plugin/plugin.exp (plugin_test_list): Add diagnostic-test-metadata-html.c. Signed-off-by: David Malcolm <dmalcolm@redhat.com>
Diffstat (limited to 'gcc')
-rw-r--r--gcc/diagnostic-format-html.cc233
-rw-r--r--gcc/testsuite/gcc.dg/html-output/missing-semicolon.py7
-rw-r--r--gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata-html.c15
-rw-r--r--gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata-html.py68
-rw-r--r--gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-2.c6
-rw-r--r--gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-2.py35
-rw-r--r--gcc/testsuite/gcc.dg/plugin/plugin.exp1
7 files changed, 326 insertions, 39 deletions
diff --git a/gcc/diagnostic-format-html.cc b/gcc/diagnostic-format-html.cc
index 2d642df..6bb1caf 100644
--- a/gcc/diagnostic-format-html.cc
+++ b/gcc/diagnostic-format-html.cc
@@ -27,11 +27,14 @@ along with GCC; see the file COPYING3. If not see
#include "diagnostic-metadata.h"
#include "diagnostic-format.h"
#include "diagnostic-format-html.h"
+#include "diagnostic-format-text.h"
#include "diagnostic-output-file.h"
#include "diagnostic-buffer.h"
#include "selftest.h"
#include "selftest-diagnostic.h"
#include "pretty-print-format-impl.h"
+#include "pretty-print-urlifier.h"
+#include "edit-context.h"
#include "intl.h"
namespace xml {
@@ -280,8 +283,8 @@ public:
friend class diagnostic_html_format_buffer;
html_builder (diagnostic_context &context,
- pretty_printer &pp,
- const line_maps *line_maps);
+ pretty_printer &pp,
+ const line_maps *line_maps);
void on_report_diagnostic (const diagnostic_info &diagnostic,
diagnostic_t orig_diag_kind,
@@ -303,11 +306,27 @@ public:
m_printer = &pp;
}
+ std::unique_ptr<xml::element>
+ make_element_for_metadata (const diagnostic_metadata &metadata);
+
+ std::unique_ptr<xml::element>
+ make_element_for_source (const diagnostic_info &diagnostic);
+
+ std::unique_ptr<xml::element>
+ make_element_for_path (const diagnostic_path &path);
+
+ std::unique_ptr<xml::element>
+ make_element_for_patch (const diagnostic_info &diagnostic);
+
private:
std::unique_ptr<xml::element>
make_element_for_diagnostic (const diagnostic_info &diagnostic,
diagnostic_t orig_diag_kind);
+ std::unique_ptr<xml::element>
+ make_metadata_element (label_text label,
+ label_text url);
+
diagnostic_context &m_context;
pretty_printer *m_printer;
const line_maps *m_line_maps;
@@ -560,28 +579,11 @@ html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
if (diagnostic.metadata)
{
- int cwe = diagnostic.metadata->get_cwe ();
- if (cwe)
- {
- diag_element->add_text (label_text::borrow (" "));
- auto cwe_span = make_span (label_text::borrow ("gcc-cwe-metadata"));
- cwe_span->add_text (label_text::borrow ("["));
- {
- auto anchor = std::make_unique<xml::element> ("a", true);
- anchor->set_attr ("href", label_text::take (get_cwe_url (cwe)));
- pretty_printer pp;
- pp_printf (&pp, "CWE-%i", cwe);
- anchor->add_text
- (label_text::take (xstrdup (pp_formatted_text (&pp))));
- cwe_span->add_child (std::move (anchor));
- }
- cwe_span->add_text (label_text::borrow ("]"));
- diag_element->add_child (std::move (cwe_span));
- }
+ diag_element->add_text (label_text::borrow (" "));
+ diag_element->add_child
+ (make_element_for_metadata (*diagnostic.metadata));
}
- // TODO: show any rules
-
label_text option_text = label_text::take
(m_context.make_option_name (diagnostic.option_id,
orig_diag_kind, diagnostic.kind));
@@ -608,20 +610,122 @@ html_builder::make_element_for_diagnostic (const diagnostic_info &diagnostic,
diag_element->add_child (std::move (option_span));
}
+ /* Source (and fix-it hints). */
+ if (auto source_element = make_element_for_source (diagnostic))
+ diag_element->add_child (std::move (source_element));
+
+ /* Execution path. */
+ if (auto path = diagnostic.richloc->get_path ())
+ if (auto path_element = make_element_for_path (*path))
+ diag_element->add_child (std::move (path_element));
+
+ if (auto patch_element = make_element_for_patch (diagnostic))
+ diag_element->add_child (std::move (patch_element));
+
+ return diag_element;
+}
+
+std::unique_ptr<xml::element>
+html_builder::make_element_for_source (const diagnostic_info &diagnostic)
+{
+ // TODO: ideally we'd like to capture elements within the following:
+ m_context.m_last_location = UNKNOWN_LOCATION;
+ pp_clear_output_area (m_printer);
+ diagnostic_show_locus (&m_context,
+ m_context.m_source_printing,
+ diagnostic.richloc, diagnostic.kind,
+ m_printer);
+ auto text = label_text::take (xstrdup (pp_formatted_text (m_printer)));
+ pp_clear_output_area (m_printer);
+
+ if (strlen (text.get ()) == 0)
+ return nullptr;
+
+ auto pre = std::make_unique<xml::element> ("pre", true);
+ pre->set_attr ("class", label_text::borrow ("gcc-annotated-source"));
+ pre->add_text (std::move (text));
+ return pre;
+}
+
+std::unique_ptr<xml::element>
+html_builder::make_element_for_path (const diagnostic_path &path)
+{
+ m_context.m_last_location = UNKNOWN_LOCATION;
+ diagnostic_text_output_format text_format (m_context);
+ pp_show_color (text_format.get_printer ()) = false;
+ pp_buffer (text_format.get_printer ())->m_flush_p = false;
+ // TODO: ideally we'd like to capture elements within the following:
+ text_format.print_path (path);
+ auto text = label_text::take
+ (xstrdup (pp_formatted_text (text_format.get_printer ())));
+
+ if (strlen (text.get ()) == 0)
+ return nullptr;
+
+ auto pre = std::make_unique<xml::element> ("pre", true);
+ pre->set_attr ("class", label_text::borrow ("gcc-execution-path"));
+ pre->add_text (std::move (text));
+ return pre;
+}
+
+std::unique_ptr<xml::element>
+html_builder::make_element_for_patch (const diagnostic_info &diagnostic)
+{
+ edit_context ec (m_context.get_file_cache ());
+ ec.add_fixits (diagnostic.richloc);
+ if (char *diff = ec.generate_diff (true))
+ if (strlen (diff) > 0)
+ {
+ auto element = std::make_unique<xml::element> ("pre", true);
+ element->set_attr ("class", label_text::borrow ("gcc-generated-patch"));
+ element->add_text (label_text::take (diff));
+ return element;
+ }
+ return nullptr;
+}
+
+std::unique_ptr<xml::element>
+html_builder::make_metadata_element (label_text label,
+ label_text url)
+{
+ auto item = make_span (label_text::borrow ("gcc-metadata-item"));
+ item->add_text (label_text::borrow ("["));
{
- auto pre = std::make_unique<xml::element> ("pre", true);
- pre->set_attr ("class", label_text::borrow ("gcc-annotated-source"));
- // TODO: ideally we'd like to capture elements within the following:
- diagnostic_show_locus (&m_context, m_context.m_source_printing,
- diagnostic.richloc, diagnostic.kind,
- m_printer);
- pre->add_text
- (label_text::take (xstrdup (pp_formatted_text (m_printer))));
- pp_clear_output_area (m_printer);
- diag_element->add_child (std::move (pre));
+ auto anchor = std::make_unique<xml::element> ("a", true);
+ anchor->set_attr ("href", std::move (url));
+ anchor->add_child (std::make_unique<xml::text> (std::move (label)));
+ item->add_child (std::move (anchor));
}
+ item->add_text (label_text::borrow ("]"));
+ return item;
+}
- return diag_element;
+std::unique_ptr<xml::element>
+html_builder::make_element_for_metadata (const diagnostic_metadata &metadata)
+{
+ auto span_metadata = make_span (label_text::borrow ("gcc-metadata"));
+
+ int cwe = metadata.get_cwe ();
+ if (cwe)
+ {
+ pretty_printer pp;
+ pp_printf (&pp, "CWE-%i", cwe);
+ label_text label = label_text::take (xstrdup (pp_formatted_text (&pp)));
+ label_text url = label_text::take (get_cwe_url (cwe));
+ span_metadata->add_child
+ (make_metadata_element (std::move (label), std::move (url)));
+ }
+
+ for (unsigned idx = 0; idx < metadata.get_num_rules (); ++idx)
+ {
+ auto &rule = metadata.get_rule (idx);
+ label_text label = label_text::take (rule.make_description ());
+ label_text url = label_text::take (rule.make_url ());
+ span_metadata->add_child
+ (make_metadata_element (std::move (label), std::move (url)));
+ }
+
+ return span_metadata;
}
/* Implementation of diagnostic_context::m_diagrams.m_emission_cb
@@ -734,6 +838,8 @@ public:
return m_builder.get_document ();
}
+ html_builder &get_builder () { return m_builder; }
+
protected:
html_output_format (diagnostic_context &context,
const line_maps *line_maps)
@@ -852,6 +958,11 @@ public:
return m_format->get_document ();
}
+ html_builder &get_builder () const
+ {
+ return m_format->get_builder ();
+ }
+
private:
class html_buffered_output_format : public html_output_format
{
@@ -880,7 +991,7 @@ test_simple_log ()
test_html_diagnostic_context dc;
rich_location richloc (line_table, UNKNOWN_LOCATION);
- dc.report (DK_ERROR, richloc, nullptr, 0, "this is a test: %i", 42);
+ dc.report (DK_ERROR, richloc, nullptr, 0, "this is a test: %qs", "foo");
const xml::document &doc = dc.get_document ();
@@ -899,20 +1010,70 @@ test_simple_log ()
" <body>\n"
" <div class=\"gcc-diagnostic-list\">\n"
" <div class=\"gcc-diagnostic\">\n"
- " <span class=\"gcc-message\">this is a test: 42</span>\n"
- " <pre class=\"gcc-annotated-source\"></pre>\n"
+ " <span class=\"gcc-message\">this is a test: `<span class=\"gcc-quoted-text\">foo</span>&apos;</span>\n"
" </div>\n"
" </div>\n"
" </body>\n"
"</html>"));
}
+static void
+test_metadata ()
+{
+ test_html_diagnostic_context dc;
+ html_builder &b = dc.get_builder ();
+
+ {
+ diagnostic_metadata metadata;
+ metadata.add_cwe (415);
+ auto element = b.make_element_for_metadata (metadata);
+ pretty_printer pp;
+ element->write_as_xml (&pp, 0, true);
+ ASSERT_STREQ
+ (pp_formatted_text (&pp),
+ "\n"
+ "<span class=\"gcc-metadata\">"
+ "<span class=\"gcc-metadata-item\">"
+ "["
+ "<a href=\"https://cwe.mitre.org/data/definitions/415.html\">"
+ "CWE-415"
+ "</a>"
+ "]"
+ "</span>"
+ "</span>");
+ }
+
+ {
+ diagnostic_metadata metadata;
+ diagnostic_metadata::precanned_rule rule ("MISC-42",
+ "http://example.com");
+ metadata.add_rule (rule);
+ auto element = b.make_element_for_metadata (metadata);
+ pretty_printer pp;
+ element->write_as_xml (&pp, 0, true);
+ ASSERT_STREQ
+ (pp_formatted_text (&pp),
+ "\n"
+ "<span class=\"gcc-metadata\">"
+ "<span class=\"gcc-metadata-item\">"
+ "["
+ "<a href=\"http://example.com\">"
+ "MISC-42"
+ "</a>"
+ "]"
+ "</span>"
+ "</span>");
+ }
+}
+
/* Run all of the selftests within this file. */
void
diagnostic_format_html_cc_tests ()
{
+ auto_fix_quotes fix_quotes;
test_simple_log ();
+ test_metadata ();
}
} // namespace selftest
diff --git a/gcc/testsuite/gcc.dg/html-output/missing-semicolon.py b/gcc/testsuite/gcc.dg/html-output/missing-semicolon.py
index 8687168..8ac1f14 100644
--- a/gcc/testsuite/gcc.dg/html-output/missing-semicolon.py
+++ b/gcc/testsuite/gcc.dg/html-output/missing-semicolon.py
@@ -60,7 +60,8 @@ def test_basics(html_tree):
pre = diag.find('xhtml:pre', ns)
assert pre is not None
- assert pre.attrib['class'] == 'gcc-annotated-source'
+ assert pre.attrib['class'] == 'gcc-generated-patch'
+ assert pre.text.startswith('--- ')
# For reference, here's the generated HTML:
"""
@@ -76,7 +77,9 @@ def test_basics(html_tree):
<div class="gcc-diagnostic-list">
<div class="gcc-diagnostic">
<span class="gcc-message">expected &apos;<span class="gcc-quoted-text">;</span>&apos; before &apos;<span class="gcc-quoted-text">}</span>&apos; token</span>
- <pre class="gcc-annotated-source"></pre>
+ <pre class="gcc-generated-patch">
+ [...snip...]
+ </pre>
</div>
</div>
</body>
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata-html.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata-html.c
new file mode 100644
index 0000000..2499e8d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata-html.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-fdiagnostics-set-output=experimental-html" } */
+/* { dg-additional-options "-fdiagnostics-show-caret" } */
+
+extern char *gets (char *s);
+
+void test_cwe (void)
+{
+ char buf[1024];
+ gets (buf);
+}
+
+/* Use a Python script to verify various properties about the generated
+ HTML file:
+ { dg-final { run-html-pytest diagnostic-test-metadata-html.c "diagnostic-test-metadata-html.py" } } */
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata-html.py b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata-html.py
new file mode 100644
index 0000000..e475e95
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-metadata-html.py
@@ -0,0 +1,68 @@
+# Verify that metadata works in HTML output.
+
+from htmltest import *
+
+import pytest
+
+@pytest.fixture(scope='function', autouse=True)
+def html_tree():
+ return html_tree_from_env()
+
+XHTML = 'http://www.w3.org/1999/xhtml'
+ns = {'xhtml': XHTML}
+
+def make_tag(local_name):
+ return f'{{{XHTML}}}' + local_name
+
+def test_metadata(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
+ assert diag.attrib['class'] == 'gcc-diagnostic'
+
+ spans = diag.findall('xhtml:span', ns)
+ metadata = spans[1]
+ assert metadata.attrib['class'] == 'gcc-metadata'
+ assert metadata[0].tag == make_tag('span')
+ assert metadata[0].attrib['class'] == 'gcc-metadata-item'
+ assert metadata[0].text == '['
+ assert metadata[0][0].tag == make_tag('a')
+ assert metadata[0][0].attrib['href'] == 'https://cwe.mitre.org/data/definitions/242.html'
+ assert metadata[0][0].text == 'CWE-242'
+ assert metadata[0][0].tail == ']'
+
+ assert metadata[1].tag == make_tag('span')
+ assert metadata[1].attrib['class'] == 'gcc-metadata-item'
+ assert metadata[1].text == '['
+ assert metadata[1][0].tag == make_tag('a')
+ assert metadata[1][0].attrib['href'] == 'https://example.com/'
+ assert metadata[1][0].text == 'STR34-C'
+ assert metadata[1][0].tail == ']'
+
+ src = diag.find('xhtml:pre', ns)
+ assert src.attrib['class'] == 'gcc-annotated-source'
+ assert src.text == (
+ ' gets (buf);\n'
+ ' ^~~~~~~~~~\n')
+
+# For reference, here's the generated HTML:
+"""
+ <body>
+ <div class="gcc-diagnostic-list">
+ <div class="gcc-diagnostic">
+ <span class="gcc-message">never use &apos;<span class="gcc-quoted-text">gets</span>&apos;</span>
+ <span class="gcc-metadata"><span class="gcc-metadata-item">[<a href="https://cwe.mitre.org/data/definitions/242.html">CWE-242</a>]</span><span class="gcc-metadata-item">[<a href="https://example.com/">STR34-C</a>]</span></span>
+ ...etc...
+ </div>
+ </div>
+ </body>
+"""
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-2.c b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-2.c
index b8134ae..26605f7 100644
--- a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-2.c
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-2.c
@@ -1,5 +1,5 @@
/* { dg-do compile } */
-/* { dg-options "-fdiagnostics-show-caret -fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events" } */
+/* { dg-options "-fdiagnostics-show-caret -fdiagnostics-show-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-add-output=experimental-html" } */
#include <stddef.h>
#include <stdlib.h>
@@ -52,3 +52,7 @@ make_a_list_of_random_ints_badly(PyObject *self,
| (3) when calling 'PyList_Append', passing NULL from (1) as argument 1
{ dg-end-multiline-output "" } */
}
+
+/* Use a Python script to verify various properties about the generated
+ HTML file:
+ { dg-final { run-html-pytest diagnostic-test-paths-2.c "diagnostic-test-paths-2.py" } } */
diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-2.py b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-2.py
new file mode 100644
index 0000000..c212e49
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/plugin/diagnostic-test-paths-2.py
@@ -0,0 +1,35 @@
+# Verify that execution paths work in HTML output.
+
+from htmltest import *
+
+import pytest
+
+@pytest.fixture(scope='function', autouse=True)
+def html_tree():
+ return html_tree_from_env()
+
+XHTML = 'http://www.w3.org/1999/xhtml'
+ns = {'xhtml': XHTML}
+
+def make_tag(local_name):
+ return f'{{{XHTML}}}' + local_name
+
+def test_paths(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
+ assert diag.attrib['class'] == 'gcc-diagnostic'
+
+ pre = diag.findall('xhtml:pre', ns)
+ assert pre[0].attrib['class'] == 'gcc-annotated-source'
+ assert pre[1].attrib['class'] == 'gcc-execution-path'
+ assert pre[1].text.startswith(" 'make_a_list_of_random_ints_badly': events 1-3")
diff --git a/gcc/testsuite/gcc.dg/plugin/plugin.exp b/gcc/testsuite/gcc.dg/plugin/plugin.exp
index a84fbae..a066b67 100644
--- a/gcc/testsuite/gcc.dg/plugin/plugin.exp
+++ b/gcc/testsuite/gcc.dg/plugin/plugin.exp
@@ -105,6 +105,7 @@ set plugin_test_list [list \
diagnostic-test-inlining-4.c } \
{ diagnostic_plugin_test_metadata.cc
diagnostic-test-metadata.c \
+ diagnostic-test-metadata-html.c \
diagnostic-test-metadata-sarif.c } \
{ diagnostic_plugin_test_nesting.cc \
diagnostic-test-nesting-text-plain.c \