In our .sarif output from e.g.:

  bad-binary-op.c: In function ‘test_4’:
  bad-binary-op.c:19:23: error: invalid operands to binary + (have ‘S’ {aka 
‘struct s’} and ‘T’ {aka ‘struct t’})
     19 |   return callee_4a () + callee_4b ();
        |          ~~~~~~~~~~~~ ^ ~~~~~~~~~~~~
        |          |              |
        |          |              T {aka struct t}
        |          S {aka struct s}

the labelled ranges are captured in the 'annotations' property of the
'location' object (§3.28.6).

However sarif-replay emits just:

  In function 'test_4':
  bad-binary-op.c:19:23: error: invalid operands to binary + (have ‘S’ {aka 
‘struct s’} and ‘T’ {aka ‘struct t’}) [error]
     19 |   return callee_4a () + callee_4b ();
        |                       ^

missing the labelled ranges.

This patch adds support to sarif-replay for the 'annotations' property;
with this patch we emit:

  In function 'test_4':
  bad-binary-op.c:19:23: error: invalid operands to binary + (have ‘S’ {aka 
‘struct s’} and ‘T’ {aka ‘struct t’}) [error]
     19 |   return callee_4a () + callee_4b ();
        |          ~~~~~~~~~~~~ ^ ~~~~~~~~~~~~
        |          |              |
        |          |              T {aka struct t}
        |          S {aka struct s}

thus showing the labelled ranges.

Doing so requires adding a new entrypoint to libgdiagnostics:
  diagnostic_physical_location_get_file
Given that we haven't yet released a stable version and that
sarif-replay is built together with libgdiagnostics I didn't
bother updating the ABI version.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
Pushed to trunk as r15-7561-ga1f63ea36e9c9f.

gcc/ChangeLog:
        PR sarif-replay/118881
        * doc/libgdiagnostics/topics/physical-locations.rst: Add
        diagnostic_physical_location_get_file.
        * libgdiagnostics++.h (physical_location::get_file): New wrapper.
        (diagnostic::add_location): Likewise.
        * libgdiagnostics.cc (diagnostic_manager::get_file_by_name): New.
        (diagnostic_physical_location::get_file): New.
        (diagnostic_physical_location_get_file): New.
        * libgdiagnostics.h (diagnostic_physical_location_get_file): New.
        * libgdiagnostics.map (diagnostic_physical_location_get_file): New.
        * libsarifreplay.cc (class annotation): New.
        (add_any_annotations): New.
        (sarif_replayer::handle_result_obj): Collect vectors of
        annotations in the calls to handle_location_object and apply them
        to "err" and to "note" as appropriate.
        (sarif_replayer::handle_thread_flow_location_object): Pass nullptr
        for annotations.
        (sarif_replayer::handle_location_object): Handle §3.28.6
        "annotations" property, using it to populate a new
        "out_annotations" param.

gcc/testsuite/ChangeLog:
        PR sarif-replay/118881
        * sarif-replay.dg/2.1.0-valid/3.28.6-annotations-1.sarif: New test.

Signed-off-by: David Malcolm <dmalc...@redhat.com>
---
 .../topics/physical-locations.rst             |  5 +
 gcc/libgdiagnostics++.h                       | 19 ++++
 gcc/libgdiagnostics.cc                        | 33 +++++++
 gcc/libgdiagnostics.h                         |  6 ++
 gcc/libgdiagnostics.map                       |  2 +
 gcc/libsarifreplay.cc                         | 94 ++++++++++++++++++-
 .../2.1.0-valid/3.28.6-annotations-1.sarif    | 47 ++++++++++
 7 files changed, 201 insertions(+), 5 deletions(-)
 create mode 100644 
gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.28.6-annotations-1.sarif

diff --git a/gcc/doc/libgdiagnostics/topics/physical-locations.rst 
b/gcc/doc/libgdiagnostics/topics/physical-locations.rst
index fec4a8f221b..099e27e9822 100644
--- a/gcc/doc/libgdiagnostics/topics/physical-locations.rst
+++ b/gcc/doc/libgdiagnostics/topics/physical-locations.rst
@@ -198,6 +198,11 @@ are at the parentheses.
 
    TODO: example of output
 
+.. function::  diagnostic_file *diagnostic_physical_location_get_file (const 
diagnostic_physical_location *physical_loc)
+
+   Get the :type:`diagnostic_file` associated with a given physical location.
+
+
 Associating diagnostics with locations
 **************************************
 
diff --git a/gcc/libgdiagnostics++.h b/gcc/libgdiagnostics++.h
index af75113678c..39477a0bc4a 100644
--- a/gcc/libgdiagnostics++.h
+++ b/gcc/libgdiagnostics++.h
@@ -93,6 +93,8 @@ public:
   : m_inner (location)
   {}
 
+  file get_file () const;
+
   const diagnostic_physical_location *m_inner;
 };
 
@@ -199,6 +201,9 @@ public:
   void
   set_location (physical_location loc);
 
+  void
+  add_location (physical_location loc);
+
   void
   add_location_with_label (physical_location loc,
                           const char *text);
@@ -372,6 +377,14 @@ file::set_buffered_content (const char *data, size_t sz)
   diagnostic_file_set_buffered_content (m_inner, data, sz);
 }
 
+// class physical_location
+
+inline file
+physical_location::get_file () const
+{
+  return file (diagnostic_physical_location_get_file (m_inner));
+}
+
 // class execution_path
 
 inline diagnostic_event_id
@@ -448,6 +461,12 @@ diagnostic::add_location_with_label (physical_location loc,
   diagnostic_add_location_with_label (m_inner, loc.m_inner, text);
 }
 
+inline void
+diagnostic::add_location (physical_location loc)
+{
+  diagnostic_add_location (m_inner, loc.m_inner);
+}
+
 inline void
 diagnostic::set_logical_location (logical_location loc)
 {
diff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc
index 89a9b7fb1d8..d274283742f 100644
--- a/gcc/libgdiagnostics.cc
+++ b/gcc/libgdiagnostics.cc
@@ -147,6 +147,8 @@ struct diagnostic_physical_location
     m_inner (inner)
   {}
 
+  diagnostic_file *get_file () const;
+
   diagnostic_manager *m_mgr;
   location_t m_inner;
 };
@@ -445,6 +447,14 @@ public:
     return file;
   }
 
+  diagnostic_file *
+  get_file_by_name (const char *name)
+  {
+    if (diagnostic_file **slot = m_str_to_file_map.get (name))
+      return *slot;
+    return nullptr;
+  }
+
   const diagnostic_physical_location *
   new_location_from_file_and_line (const diagnostic_file *file,
                                   diagnostic_line_num_t line_num)
@@ -943,6 +953,18 @@ diagnostic_file::set_buffered_content (const char *buf, 
size_t sz)
   fc.add_buffered_content (m_name.get_str (), buf, sz);
 }
 
+// struct diagnostic_physical_location
+
+diagnostic_file *
+diagnostic_physical_location::get_file () const
+{
+  m_mgr->set_line_table_global ();
+  const char *filename = LOCATION_FILE (m_inner);
+  if (!filename)
+    return nullptr;
+  return m_mgr->get_file_by_name (filename);
+}
+
 /* class impl_diagnostic_client_data_hooks.  */
 
 const client_version_info *
@@ -1753,3 +1775,14 @@ diagnostic_finish_va (diagnostic *diag, const char 
*gmsgid, va_list *args)
   diag->get_manager ().emit (*diag, gmsgid, args);
   delete diag;
 }
+
+/* Public entrypoint.  */
+
+diagnostic_file *
+diagnostic_physical_location_get_file (const diagnostic_physical_location 
*physical_loc)
+{
+  if (!physical_loc)
+    return nullptr;
+
+  return physical_loc->get_file ();
+}
diff --git a/gcc/libgdiagnostics.h b/gcc/libgdiagnostics.h
index f2041476786..2ce0f4c99c8 100644
--- a/gcc/libgdiagnostics.h
+++ b/gcc/libgdiagnostics.h
@@ -686,6 +686,12 @@ diagnostic_finish_va (diagnostic *diag, const char *fmt, 
va_list *args)
   LIBGDIAGNOSTICS_PARAM_MUST_BE_NON_NULL (2)
   LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (2, 0);
 
+/* Get the diagnostic_file associated with PHYSICAL_LOC.  */
+
+extern diagnostic_file *
+diagnostic_physical_location_get_file (const diagnostic_physical_location 
*physical_loc)
+  LIBGDIAGNOSTICS_PARAM_CAN_BE_NULL(0);
+
 /* DEFERRED:
    - thread-safety
    - plural forms
diff --git a/gcc/libgdiagnostics.map b/gcc/libgdiagnostics.map
index 995e684a74b..5958cfe3f12 100644
--- a/gcc/libgdiagnostics.map
+++ b/gcc/libgdiagnostics.map
@@ -69,5 +69,7 @@ LIBGDIAGNOSTICS_ABI_0
     diagnostic_finish;
     diagnostic_finish_va;
 
+    diagnostic_physical_location_get_file;
+
   local: *;
 };
diff --git a/gcc/libsarifreplay.cc b/gcc/libsarifreplay.cc
index 71f80797926..075dadba41f 100644
--- a/gcc/libsarifreplay.cc
+++ b/gcc/libsarifreplay.cc
@@ -199,6 +199,23 @@ struct string_property_value
   ValueType m_value;
 };
 
+/* A class for recording annotations seen in locations (§3.28.6) that
+   should be emitted as secondary locations on diagnostics.  */
+
+class annotation
+{
+public:
+  annotation (libgdiagnostics::physical_location phys_loc,
+             label_text label)
+  : m_phys_loc (phys_loc),
+    m_label (std::move (label))
+  {
+  }
+
+  libgdiagnostics::physical_location m_phys_loc;
+  label_text m_label;
+};
+
 class sarif_replayer
 {
 public:
@@ -287,7 +304,8 @@ private:
   handle_location_object (const json::object &location_obj,
                          const json::object &run_obj,
                          libgdiagnostics::physical_location &out_physical_loc,
-                         libgdiagnostics::logical_location &out_logical_loc);
+                         libgdiagnostics::logical_location &out_logical_loc,
+                         std::vector<annotation> *out_annotations);
 
   // "physicalLocation" object (§3.29)
   enum status
@@ -962,6 +980,18 @@ sarif_replayer::get_level_from_level_str (const 
json::string &level_str)
      level_values, ARRAY_SIZE (level_values));
 }
 
+static void
+add_any_annotations (libgdiagnostics::diagnostic &diag,
+                    const std::vector<annotation> &annotations)
+{
+  for (auto &annotation : annotations)
+    if (annotation.m_label.get ())
+      diag.add_location_with_label (annotation.m_phys_loc,
+                                   annotation.m_label.get ());
+    else
+      diag.add_location (annotation.m_phys_loc);
+}
+
 /* Process a result object (SARIF v2.1.0 section 3.27).
    Known limitations:
    - doesn't yet handle "ruleIndex" property (§3.27.6)
@@ -1025,6 +1055,7 @@ sarif_replayer::handle_result_obj (const json::object 
&result_obj,
     = get_required_property<json::array> (result_obj, locations_prop);
   if (!locations_arr)
     return status::err_invalid_sarif;
+  std::vector<annotation> annotations;
   if (locations_arr->length () > 0)
     {
       /* Only look at the first, if there's more than one.  */
@@ -1035,7 +1066,8 @@ sarif_replayer::handle_result_obj (const json::object 
&result_obj,
        return status::err_invalid_sarif;
       enum status s = handle_location_object (*location_obj, run_obj,
                                              physical_loc,
-                                             logical_loc);
+                                             logical_loc,
+                                             &annotations);
       if (s != status::ok)
        return s;
     }
@@ -1092,6 +1124,7 @@ sarif_replayer::handle_result_obj (const json::object 
&result_obj,
     }
   err.set_location (physical_loc);
   err.set_logical_location (logical_loc);
+  add_any_annotations (err, annotations);
   if (path.m_inner)
     err.take_execution_path (std::move (path));
   err.finish ("%s", text.get ());
@@ -1112,9 +1145,11 @@ sarif_replayer::handle_result_obj (const json::object 
&result_obj,
                                          prop_related_locations);
          if (!location_obj)
            return status::err_invalid_sarif;
+         std::vector<annotation> annotations;
          enum status s = handle_location_object (*location_obj, run_obj,
                                                  physical_loc,
-                                                 logical_loc);
+                                                 logical_loc,
+                                                 &annotations);
          if (s != status::ok)
            return s;
 
@@ -1136,6 +1171,7 @@ sarif_replayer::handle_result_obj (const json::object 
&result_obj,
              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);
              note.finish ("%s", text.get ());
            }
        }
@@ -1490,7 +1526,8 @@ handle_thread_flow_location_object (const json::object 
&tflow_loc_obj,
     {
       /* location object (§3.28).  */
       enum status s = handle_location_object (*location_obj, run_obj,
-                                             physical_loc, logical_loc);
+                                             physical_loc, logical_loc,
+                                             nullptr);
       if (s != status::ok)
        return s;
 
@@ -1564,7 +1601,8 @@ sarif_replayer::
 handle_location_object (const json::object &location_obj,
                        const json::object &run_obj,
                        libgdiagnostics::physical_location &out_physical_loc,
-                       libgdiagnostics::logical_location &out_logical_loc)
+                       libgdiagnostics::logical_location &out_logical_loc,
+                       std::vector<annotation> *out_annotations)
 {
   // §3.28.3 "physicalLocation" property
   {
@@ -1604,6 +1642,52 @@ handle_location_object (const json::object &location_obj,
        }
   }
 
+  // §3.28.6 "annotations" property
+  {
+    const property_spec_ref annotations_prop
+      ("location", "annotations", "3.28.6");
+    if (const json::array *annotations_arr
+       = get_optional_property<json::array> (location_obj,
+                                             annotations_prop))
+      for (auto element : *annotations_arr)
+       {
+         const json::object *annotation_obj
+           = require_object_for_element (*element, annotations_prop);
+         if (!annotation_obj)
+           return status::err_invalid_sarif;
+         libgdiagnostics::file file = out_physical_loc.get_file ();
+         if (!file.m_inner)
+           return report_invalid_sarif
+             (*annotation_obj, annotations_prop,
+              "cannot find artifact for %qs property",
+              annotations_prop.get_property_name ());
+         libgdiagnostics::physical_location phys_loc;
+         enum status s = handle_region_object (*annotation_obj,
+                                               file,
+                                               phys_loc);
+         if (s != status::ok)
+           return s;
+
+         label_text label;
+
+         // §3.30.14 message property
+         {
+           const property_spec_ref message_prop
+             ("region", "message", "3.30.14");
+
+           if (const json::object *message_obj
+                 = get_optional_property<json::object> (*annotation_obj,
+                                                        message_prop))
+             label = make_plain_text_within_result_message (nullptr,
+                                                            *message_obj,
+                                                            nullptr);
+         }
+
+         if (out_annotations)
+           out_annotations->push_back ({phys_loc, std::move (label)});
+       }
+  }
+
   return status::ok;
 }
 
diff --git 
a/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.28.6-annotations-1.sarif 
b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.28.6-annotations-1.sarif
new file mode 100644
index 00000000000..4ff6e07ab4d
--- /dev/null
+++ b/gcc/testsuite/sarif-replay.dg/2.1.0-valid/3.28.6-annotations-1.sarif
@@ -0,0 +1,47 @@
+{"$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 15.0.1 
20250203 (experimental) (x86_64-pc-linux-gnu)",
+                               "version": "15.0.1 20250203 (experimental)",
+                               "informationUri": "https://gcc.gnu.org/gcc-15/";,
+                               "rules": []}},
+           "invocations": [{"executionSuccessful": false,
+                            "toolExecutionNotifications": []}],
+           "artifacts": [{"location": {"uri": 
"bad-binary-ops-highlight-colors.c"},
+                          "sourceLanguage": "c",
+                          "contents": {"text": "/* Verify that colorization 
affects both text within diagnostic messages\n   and underlined ranges of 
quoted source, and that the types we use\n   match up between them.\n   Also 
implicitly verify that -fdiagnostics-show-highlight-colors is\n   on by 
default.  */\n\n\n\nstruct s {};\nstruct t {};\ntypedef struct s S;\ntypedef 
struct t T;\n\nextern S callee_4a (void);\nextern T callee_4b (void);\n\nint 
test_4 (void)\n{\n  return callee_4a () + callee_4b ();\n}\n"},
+                          "roles": ["analysisTarget"]}],
+           "results": [{"ruleId": "error",
+                        "level": "error",
+                        "message": {"text": "invalid operands to binary + 
(have ‘S’ {{aka ‘struct s’}} and ‘T’ {{aka ‘struct t’}})"},
+                        "locations": [{"physicalLocation": 
{"artifactLocation": {"uri": "bad-binary-ops-highlight-colors.c",
+                                                                               
  "uriBaseId": "PWD"},
+                                                            "region": 
{"startLine": 19,
+                                                                       
"startColumn": 23,
+                                                                       
"endColumn": 24},
+                                                            "contextRegion": 
{"startLine": 19,
+                                                                              
"snippet": {"text": "  return callee_4a () + callee_4b ();\n"}}},
+                                       "logicalLocations": [{"name": "test_4",
+                                                             
"fullyQualifiedName": "test_4",
+                                                             "decoratedName": 
"test_4",
+                                                             "kind": 
"function"}],
+                                       "annotations": [{"startLine": 19,
+                                                        "startColumn": 10,
+                                                        "endColumn": 22,
+                                                        "message": {"text": "S 
{{aka struct s}}"}},
+                                                       {"startLine": 19,
+                                                        "startColumn": 25,
+                                                        "endColumn": 37,
+                                                        "message": {"text": "T 
{{aka struct t}}"}}]}]}]}]}
+
+/* Verify that we underline and label the ranges for the
+   "annotations" above.  */
+/* { dg-begin-multiline-output "" }
+bad-binary-ops-highlight-colors.c:19:23: error: invalid operands to binary + 
(have ‘S’ {aka ‘struct s’} and ‘T’ {aka ‘struct t’}) [error]
+   19 |   return callee_4a () + callee_4b ();
+      |          ~~~~~~~~~~~~ ^ ~~~~~~~~~~~~
+      |          |              |
+      |          |              T {aka struct t}
+      |          S {aka struct s}
+   { dg-end-multiline-output "" } */
+// TODO: trailing [error]
-- 
2.26.3

Reply via email to