OpenMP: Update documentation of metadirective implementation status.
[gcc.git] / gcc / libsarifreplay.cc
blobeb829064194d8c65fec472f8da7d32734f5aec28
1 /* A library for re-emitting diagnostics saved in SARIF form
2 via libgdiagnostics.
3 Copyright (C) 2022-2025 Free Software Foundation, Inc.
4 Contributed by David Malcolm <dmalcolm@redhat.com>.
6 This file is part of GCC.
8 GCC is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 3, or (at your option) any later
11 version.
13 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
18 You should have received a copy of the GNU General Public License
19 along with GCC; see the file COPYING3. If not see
20 <http://www.gnu.org/licenses/>. */
22 #include "config.h"
23 #define INCLUDE_VECTOR
24 #define INCLUDE_MAP
25 #define INCLUDE_STRING
26 #include "system.h"
27 #include "coretypes.h"
28 #include "make-unique.h"
29 #include "libgdiagnostics++.h"
30 #include "json-parsing.h"
31 #include "intl.h"
32 #include "sarif-spec-urls.def"
33 #include "libsarifreplay.h"
34 #include "label-text.h"
36 namespace {
38 /* Read the contents of PATH into memory.
39 Issue an error to MGR and return nullptr if there are any problems. */
41 static std::unique_ptr<std::vector<char>>
42 read_file (const char *path, libgdiagnostics::manager &mgr)
44 FILE *f_in = fopen (path, "r");
45 if (!f_in)
47 char *errmsg = xstrerror (errno);
48 auto err (mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR));
49 err.finish ("cannot open %qs: %s", path, errmsg);
50 return nullptr;
53 /* Read content, allocating a buffer for it. */
54 auto result = ::make_unique<std::vector<char>> ();
55 char buf[4096];
56 size_t iter_sz_in;
58 while ( (iter_sz_in = fread (buf, 1, sizeof (buf), f_in)) )
60 size_t old_total_sz = result->size ();
61 size_t new_total_sz = old_total_sz + iter_sz_in;
62 size_t old_alloc_sz = result->capacity ();
63 if (new_total_sz > old_alloc_sz)
65 size_t new_alloc_sz = std::max (old_alloc_sz * 2, new_total_sz);
66 result->reserve (new_alloc_sz);
68 gcc_assert (result->capacity () >= new_total_sz);
69 result->resize (new_total_sz);
70 memcpy (result->data () + old_total_sz, buf, iter_sz_in);
73 if (!feof (f_in))
75 char *errmsg = xstrerror (errno);
76 auto err (mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR));
77 err.finish ("error reading from %qs: %s", path, errmsg);
78 return nullptr;
81 fclose (f_in);
83 return result;
86 static libgdiagnostics::physical_location
87 make_physical_location (libgdiagnostics::manager &mgr,
88 libgdiagnostics::file f,
89 const json::location_map::point &point)
91 /* json::location_map::point uses 0-based columns,
92 whereas libgdiagnostics uses 1-based columns. */
93 return mgr.new_location_from_file_line_column (f,
94 point.m_line,
95 point.m_column + 1);
98 static libgdiagnostics::physical_location
99 make_physical_location (libgdiagnostics::manager &mgr,
100 libgdiagnostics::file f,
101 const json::location_map::range &range)
103 libgdiagnostics::physical_location start
104 = make_physical_location (mgr, f, range.m_start);
105 libgdiagnostics::physical_location end
106 = make_physical_location (mgr, f, range.m_end);
107 return mgr.new_location_from_range (start, start, end);
110 enum class status
113 err_reading_file,
114 err_malformed_json,
115 err_invalid_sarif,
116 err_unhandled_sarif
119 /* A reference to the SARIF specification. */
121 class spec_ref
123 public:
124 spec_ref (const char *section)
125 : m_section (section)
128 virtual char *make_description () const
130 /* 'SECTION SIGN' (U+00A7). */
131 #define SECTION_SIGN_UTF8 "\xC2\xA7"
132 return xasprintf ("SARIF v2.1.0 " SECTION_SIGN_UTF8 "%s", m_section);
135 char *make_url () const
137 const char *anchor = get_anchor_for_section (m_section);
138 if (!anchor)
139 return nullptr;
140 return xasprintf ("%s#%s", sarif_spec_base_url, anchor);
143 private:
144 static const char *
145 get_anchor_for_section (const char *section)
147 /* Linear search, but the array is only a few hundred entries . */
148 for (size_t i = 0; i < ARRAY_SIZE (sarif_spec_anchor_arr); i++)
150 if (strcmp (sarif_spec_anchor_arr[i].m_ref, section) == 0)
151 return sarif_spec_anchor_arr[i].m_anchor;
153 return nullptr;
156 /* e.g. "3.1" for section 3.1 of the spec. */
157 const char *m_section;
160 /* A reference to the SARIF specification for a particular kind of object. */
162 class object_spec_ref : public spec_ref
164 public:
165 object_spec_ref (const char *obj_name, const char *section)
166 : spec_ref (section),
167 m_obj_name (obj_name)
170 const char *get_obj_name () const { return m_obj_name; }
172 private:
173 const char *m_obj_name;
176 /* A reference to the SARIF specification for a particular property
177 of a particular kind of object. */
179 class property_spec_ref : public object_spec_ref
181 public:
182 property_spec_ref (const char *obj_name,
183 const char *property_name,
184 const char *section)
185 : object_spec_ref (obj_name, section),
186 m_property_name (property_name)
189 const char *get_property_name () const { return m_property_name; }
191 private:
192 const char *m_property_name;
195 template <typename ValueType>
196 struct string_property_value
198 const char *m_string;
199 ValueType m_value;
202 class sarif_replayer
204 public:
205 sarif_replayer (libgdiagnostics::manager &&output_manager,
206 libgdiagnostics::manager &&control_manager)
207 : m_output_mgr (std::move (output_manager)),
208 m_control_mgr (std::move (control_manager)),
209 m_driver_obj (nullptr),
210 m_artifacts_arr (nullptr)
214 enum status replay_file (const char *filename,
215 const replay_options &replay_opts);
217 private:
218 class replayer_location_map : public json::location_map
220 public:
221 void record_range_for_value (json::value *jv,
222 const range &r) final override
224 m_map_jv_to_range[jv] = r;
227 const json::location_map::range &
228 get_range_for_value (const json::value &jv) const
230 auto iter = m_map_jv_to_range.find (&jv);
231 gcc_assert (iter != m_map_jv_to_range.end ());
232 return iter->second;
235 private:
236 std::map<const json::value *, range> m_map_jv_to_range;
239 enum status emit_sarif_as_diagnostics (const json::value &jv);
241 label_text
242 make_plain_text_within_result_message (const json::object *tool_component_obj,
243 const json::object &message_obj,
244 const json::object *rule_obj);
246 /* Handlers for specific parts of the SARIF spec.
247 Keep this in the same order as the spec. */
249 // "artifactLocation" object (§3.4)
250 enum status
251 handle_artifact_location_object (const json::object &artifact_loc,
252 libgdiagnostics::file &out);
254 // Message string lookup algorithm (§3.11.7)
255 const char *
256 lookup_plain_text_within_result_message (const json::object *tool_component_obj,
257 const json::object &message_obj,
258 const json::object *rule_obj);
260 // "multiformatMessageString" object (§3.12).
261 const char *
262 get_plain_text_from_mfms (json::value &mfms_val,
263 const property_spec_ref &prop);
265 // "run" object (§3.14)
266 enum status
267 handle_run_obj (const json::object &run_obj);
269 // "tool" object (§3.18)
270 enum status
271 handle_tool_obj (const json::object &tool_obj);
273 // "artifact" object (§3.24). */
274 void
275 handle_artifact_obj (const json::object &artifact_obj);
277 // "result" object (§3.27)
278 enum status
279 handle_result_obj (const json::object &result_obj,
280 const json::object &tool_obj);
281 json::result<enum diagnostic_level, enum status>
282 get_level_from_level_str (const json::string &level_str);
284 // "location" object (§3.28)
285 enum status
286 handle_location_object (const json::object &location_obj,
287 libgdiagnostics::physical_location &out_physical_loc,
288 libgdiagnostics::logical_location &out_logical_loc);
290 // "physicalLocation" object (§3.29)
291 enum status
292 handle_physical_location_object (const json::object &phys_loc_obj,
293 libgdiagnostics::physical_location &out);
295 // "region" object (§3.30)
296 enum status
297 handle_region_object (const json::object &region_obj,
298 libgdiagnostics::file file,
299 libgdiagnostics::physical_location &out);
301 // "logicalLocation" object (§3.33)
302 enum status
303 handle_logical_location_object (const json::object &logical_loc_obj,
304 libgdiagnostics::logical_location &out);
306 // "threadFlow" object (§3.37)
307 enum status
308 handle_thread_flow_object (const json::object &thread_flow_obj,
309 libgdiagnostics::execution_path &out);
311 // "threadFlowLocation" object (§3.38)
312 enum status
313 handle_thread_flow_location_object (const json::object &tflow_loc_obj,
314 libgdiagnostics::execution_path &out);
316 // reportingDescriptor lookup (§3.52.3)
317 const json::object *
318 lookup_rule_by_id_in_tool (const char *rule_id,
319 const json::object &tool_obj,
320 const json::object *&tool_component_obj);
322 const json::object *
323 lookup_rule_by_id_in_component (const char *rule_id,
324 const json::object &tool_component_obj);
326 /* Support functions. */
328 /* Report an error to m_control_mgr about JV violating REF,
329 and return status::err_invalid_sarif. */
331 enum status
332 report_invalid_sarif (const json::value &jv,
333 const spec_ref &ref,
334 const char *gmsgid, ...)
335 LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (4, 5)
337 va_list ap;
338 va_start (ap, gmsgid);
339 report_problem (jv, ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_ERROR);
340 va_end (ap);
341 return status::err_invalid_sarif;
344 /* Report a "sorry" to m_control_mgr inability to handle JV and REF,
345 and return status::err_unhandled_sarif. */
347 enum status
348 report_unhandled_sarif (const json::value &jv,
349 const spec_ref &ref,
350 const char *gmsgid, ...)
351 LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (4, 5)
353 va_list ap;
354 va_start (ap, gmsgid);
355 report_problem (jv, ref, gmsgid, &ap, DIAGNOSTIC_LEVEL_SORRY);
356 va_end (ap);
357 return status::err_unhandled_sarif;
360 void
361 report_problem (const json::value &jv,
362 const spec_ref &ref,
363 const char *gmsgid,
364 va_list *args,
365 enum diagnostic_level level)
366 LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (4, 0)
368 auto diag (m_control_mgr.begin_diagnostic (level));
370 /* Add rule specifying the pertinent section of the specification.
371 There doesn't seem to be a systematic mapping from spec sections to
372 HTML anchors, so we can't provide URLs
373 (filed as https://github.com/oasis-tcs/sarif-spec/issues/533 ). */
374 char *ref_desc = ref.make_description ();
375 char *ref_url = ref.make_url ();
376 diag.add_rule (ref_desc, ref_url);
377 free (ref_desc);
378 free (ref_url);
380 auto loc_range
381 = make_physical_location (m_control_mgr,
382 m_loaded_file,
383 m_json_location_map.get_range_for_value (jv));
384 diag.set_location (loc_range);
386 diag.finish_va (gmsgid, args);
389 /* Require OBJ to have at least one of OBJ_PROP1 or OBJ_PROP2.
390 If successful, result status::ok.
391 Otherwise, complain about OBJ_CONSTRAINTS and return
392 status::invalid_sarif. */
393 enum status
394 report_invalid_sarif_at_least_one_of (const json::object &obj,
395 const object_spec_ref &obj_constraints,
396 const property_spec_ref &obj_prop_1,
397 const property_spec_ref &obj_prop_2)
399 return report_invalid_sarif
400 (obj, obj_constraints,
401 "expected SARIF %qs object to contain at least one of %qs or %qs",
402 obj_constraints.get_obj_name (),
403 obj_prop_1.get_property_name (),
404 obj_prop_2.get_property_name ());
407 /* Require VAL to be a json::object.
408 If successful, return it as an object.
409 Otherwise, complain using REF and return nullptr. */
410 const json::object *
411 require_object (const json::value &val, const property_spec_ref &ref)
413 const json::object *obj = dyn_cast <const json::object *> (&val);
414 if (!obj)
416 report_invalid_sarif (val, ref, "expected %s.%s to be an object",
417 ref.get_obj_name (), ref.get_property_name ());
418 return nullptr;
420 return obj;
423 /* Require VAL to be a json::string
424 If successful, return it as an string.
425 Otherwise, complain using REF and return nullptr. */
426 const json::string *
427 require_string (const json::value &val, const property_spec_ref &ref)
429 const json::string *str = dyn_cast <const json::string *> (&val);
430 if (!str)
432 report_invalid_sarif (val, ref, "expected %s.%s to be an string",
433 ref.get_obj_name (), ref.get_property_name ());
434 return nullptr;
436 return str;
438 /* Look for an optional property within OBJ based on REF. */
439 const json::value *
440 get_optional_property (const json::object &obj,
441 const property_spec_ref &ref)
443 return obj.get (ref.get_property_name ());
446 /* Look for a property of VAL based on REF.
447 If present, it must be of kind JsonType.
448 If found and valid, return the property's value.
449 If not found, silently return nullptr.
450 Otherwise, complain and return nullptr. */
451 template <typename JsonType>
452 const JsonType *
453 get_optional_property (const json::object &obj,
454 const property_spec_ref &ref)
456 const json::value *property_val = get_optional_property (obj, ref);
457 if (!property_val)
458 return nullptr;
459 const JsonType *sub = dyn_cast<const JsonType *> (property_val);
460 if (!sub)
462 /* Property is wrong kind of value. */
463 report_bad_property_kind<JsonType> (obj, ref, *property_val);
464 return nullptr;
466 return sub;
469 /* Require VAL to be a json::object.
470 Look for a property of VAL based on REF, which must be of
471 kind JsonType.
472 If successful, return the property's value.
473 Otherwise, complain and return nullptr. */
474 template <typename JsonType>
475 const JsonType *
476 get_required_property (const json::value &val,
477 const property_spec_ref &ref)
479 const json::object *obj = require_object (val, ref);
480 if (!obj)
481 return nullptr;
482 return get_required_property<JsonType> (*obj, ref);
485 /* Look for a property of VAL based on REF, which must be of
486 kind JsonType.
487 If successful, return the property's value.
488 Otherwise, complain and return nullptr. */
489 template <typename JsonType>
490 const JsonType *
491 get_required_property (const json::object &obj,
492 const property_spec_ref &ref)
494 const json::value *property_val = get_optional_property (obj, ref);
495 if (!property_val)
497 /* Property not present. */
498 report_invalid_sarif (obj, ref,
499 "expected %s object to have a %qs property",
500 ref.get_obj_name (), ref.get_property_name ());
501 return nullptr;
503 const JsonType *sub = dyn_cast<const JsonType *> (property_val);
504 if (!sub)
506 /* Property is wrong kind of value. */
507 report_bad_property_kind<JsonType> (obj, ref, *property_val);
508 return nullptr;
510 return sub;
513 template <typename JsonType>
514 void
515 report_bad_property_kind (const json::object &obj,
516 const property_spec_ref &ref,
517 const json::value &property_val);
519 const json::object *
520 require_object_for_element (const json::value &jv,
521 const property_spec_ref &ref)
523 const json::object *obj = dyn_cast <const json::object *> (&jv);
524 if (!obj)
526 report_invalid_sarif (jv, ref,
527 "expected element of %s.%s array to be an object",
528 ref.get_obj_name (), ref.get_property_name ());
529 return nullptr;
531 return obj;
534 template <typename ValueType>
535 json::result<ValueType, enum status>
536 get_value_from_json_string (const json::string &json_str,
537 const property_spec_ref &prop,
538 const string_property_value<ValueType> *value_arr,
539 size_t num_values);
541 /* The manager to replay the SARIF files to. */
542 libgdiagnostics::manager m_output_mgr;
544 /* The manager for reporting issues loading SARIF files. */
545 libgdiagnostics::manager m_control_mgr;
547 /* The file within m_control_mgr representing the .sarif file. */
548 libgdiagnostics::file m_loaded_file;
550 replayer_location_map m_json_location_map;
552 const json::object *m_driver_obj;
553 const json::array *m_artifacts_arr;
556 static const char *
557 describe_kind (const json::value &val)
559 switch (val.get_kind ())
561 default:
562 gcc_unreachable ();
563 case json::JSON_OBJECT:
564 return _("JSON object");
566 case json::JSON_ARRAY:
567 return _("JSON array");
569 case json::JSON_INTEGER:
570 case json::JSON_FLOAT:
571 return _("JSON number");
573 case json::JSON_STRING:
574 return _("JSON string");
576 case json::JSON_TRUE:
577 case json::JSON_FALSE:
578 case json::JSON_NULL:
579 return _("JSON literal");
583 /* class sarif_replayer. */
585 template <>
586 void
587 sarif_replayer::
588 report_bad_property_kind<json::value> (const json::object &,
589 const property_spec_ref &,
590 const json::value &)
592 gcc_unreachable ();
595 template <>
596 void
597 sarif_replayer::
598 report_bad_property_kind<json::integer_number> (const json::object &,
599 const property_spec_ref &ref,
600 const json::value &propval)
602 report_invalid_sarif (propval, ref, "expected %s.%s to be a JSON integer; got %s",
603 ref.get_obj_name (), ref.get_property_name (),
604 describe_kind (propval));
607 template <>
608 void
609 sarif_replayer::
610 report_bad_property_kind<json::string> (const json::object &,
611 const property_spec_ref &ref,
612 const json::value &propval)
614 report_invalid_sarif (propval, ref, "expected %s.%s to be a JSON string; got %s",
615 ref.get_obj_name (), ref.get_property_name (),
616 describe_kind (propval));
619 template <>
620 void
621 sarif_replayer::
622 report_bad_property_kind<json::array> (const json::object &,
623 const property_spec_ref &ref,
624 const json::value &propval)
626 report_invalid_sarif (propval, ref, "expected %s.%s to be a JSON array; got %s",
627 ref.get_obj_name (), ref.get_property_name (),
628 describe_kind (propval));
631 template <>
632 void
633 sarif_replayer::
634 report_bad_property_kind<json::object> (const json::object &,
635 const property_spec_ref &ref,
636 const json::value &propval)
638 report_invalid_sarif (propval, ref, "expected %s.%s to be a JSON object; got %s",
639 ref.get_obj_name (), ref.get_property_name (),
640 describe_kind (propval));
643 enum status
644 sarif_replayer::replay_file (const char *filename,
645 const replay_options &replay_opts)
647 std::unique_ptr<std::vector<char>> buf = read_file (filename, m_control_mgr);
648 if (!buf)
649 return status::err_reading_file;
651 /* Use "sarif" as the sourceLanguage for SARIF itself; see
652 https://github.com/oasis-tcs/sarif-spec/issues/654 */
653 const char * const source_language = "sarif";
654 m_loaded_file = m_control_mgr.new_file (filename, source_language);
656 if (replay_opts.m_echo_file)
658 fprintf (stderr, "%s: (%li bytes)\n",
659 filename, (long)buf->size ());
660 for (size_t i = 0; i < buf->size(); i++)
661 fputc ((*buf)[i], stderr);
664 json::parser_result_t result
665 (json::parse_utf8_string (buf->size (),
666 (const char *)buf->data (),
667 replay_opts.m_json_comments,
668 &m_json_location_map));
670 if (auto json_err = result.m_err.get ())
672 gcc_assert (!result.m_val.get ());
673 auto file = m_control_mgr.new_file (filename, source_language);
674 auto loc_range = make_physical_location (m_control_mgr,
675 file,
676 json_err->get_range ());
677 auto err (m_control_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR));
678 err.set_location (loc_range);
679 err.finish ("%s", json_err->get_msg ());
681 return status::err_malformed_json;
684 gcc_assert (result.m_val.get ());
685 return emit_sarif_as_diagnostics (*result.m_val.get ());
688 #define PROP_sarifLog_version \
689 property_spec_ref ("sarifLog", "version", "3.13.2")
691 #define PROP_sarifLog_runs \
692 property_spec_ref ("sarifLog", "runs", "3.13.4")
694 enum status
695 sarif_replayer::emit_sarif_as_diagnostics (const json::value &jv)
697 /* We expect a sarifLog object as the top-level value
698 (SARIF v2.1.0 section 3.13). */
699 const json::object *toplev_obj = dyn_cast <const json::object *> (&jv);
700 if (!toplev_obj)
701 return report_invalid_sarif
702 (jv, spec_ref ("3.1"),
703 "expected a sarifLog object as the top-level value");
705 /* sarifLog objects SHALL have a property named "version"
706 (SARIF v2.1.0 section 3.13.2) with a string value. */
707 if (!get_required_property<json::string> (*toplev_obj,
708 PROP_sarifLog_version))
709 return status::err_invalid_sarif;
711 /* sarifLog.runs must be null or be an array. */
712 const property_spec_ref prop_runs (PROP_sarifLog_runs);
713 const json::value *runs
714 = get_required_property<json::value> (*toplev_obj, prop_runs);
715 if (!runs)
716 return status::err_invalid_sarif;
718 switch (runs->get_kind ())
720 default:
721 return report_invalid_sarif (*runs, prop_runs,
722 "expected sarifLog.runs to be"
723 " %<null%> or an array");
725 case json::JSON_NULL:
726 /* Nothing to do. */
727 break;
729 case json::JSON_ARRAY:
731 const json::array &runs_arr = *as_a <const json::array *> (runs);
732 for (auto element : runs_arr)
734 const json::object *run_obj
735 = require_object_for_element (*element, prop_runs);
736 if (!run_obj)
737 return status::err_invalid_sarif;
738 enum status s = handle_run_obj (*run_obj);
739 if (s != status::ok)
740 return s;
743 break;
746 return status::ok;
749 /* Process a run object (SARIF v2.1.0 section 3.14). */
751 enum status
752 sarif_replayer::handle_run_obj (const json::object &run_obj)
754 const json::object *tool_obj
755 = get_required_property<json::object> (run_obj,
756 property_spec_ref ("run", "tool",
757 "3.14.6"));
758 if (!tool_obj)
759 return status::err_invalid_sarif;
761 enum status err = handle_tool_obj (*tool_obj);
762 if (err != status::ok)
763 return err;
766 m_driver_obj
767 = get_required_property<json::object> (*tool_obj,
768 property_spec_ref ("tool", "driver",
769 "3.18.2"));
770 if (!m_driver_obj)
771 return status::err_invalid_sarif;
773 const property_spec_ref prop_artifacts ("run", "artifacts", "3.14.15");
774 m_artifacts_arr
775 = get_optional_property<json::array> (run_obj, prop_artifacts);
776 if (m_artifacts_arr)
777 for (auto element : *m_artifacts_arr)
779 if (const json::object *artifact_obj
780 = require_object_for_element (*element, prop_artifacts))
781 handle_artifact_obj (*artifact_obj);
782 else
783 return status::err_invalid_sarif;
786 /* If present, run.results must be null or be an array. */
787 const property_spec_ref prop_results ("run", "results", "3.14.23");
788 if (const json::value *results = get_optional_property (run_obj,
789 prop_results))
790 switch (results->get_kind ())
792 default:
793 return report_invalid_sarif (*results, prop_results,
794 "expected run.results to be"
795 " %<null%> or an array");
797 case json::JSON_NULL:
798 /* Nothing to do. */
799 break;
800 case json::JSON_ARRAY:
802 const json::array *results_arr = as_a <const json::array *> (results);
803 for (auto element : *results_arr)
805 const json::object *result_obj
806 = require_object_for_element (*element, prop_results);
807 if (!result_obj)
808 return status::err_invalid_sarif;
809 enum status s = handle_result_obj (*result_obj, *tool_obj);
810 if (s != status::ok)
811 return s;
814 break;
817 return status::ok;
820 /* Process an artifact object (SARIF v2.1.0 section 3.24).
821 Create a libgdiagnostics::file for each artifact that has a uri,
822 effectively prepopulating a cache with source language and contents. */
824 void
825 sarif_replayer::handle_artifact_obj (const json::object &artifact_obj)
827 const property_spec_ref location ("artifact", "location", "3.24.2");
828 auto artifact_loc_obj
829 = get_optional_property<json::object> (artifact_obj, location);
830 if (!artifact_loc_obj)
831 return;
833 // we should now have an artifactLocation object (§3.4)
835 // 3.4.3 uri property
836 const property_spec_ref prop_uri ("artifactLocation", "uri", "3.4.3");
837 auto artifact_loc_uri
838 = get_optional_property<json::string> (*artifact_loc_obj, prop_uri);
839 if (!artifact_loc_uri)
840 return;
842 const char *sarif_source_language = nullptr;
843 const property_spec_ref prop_source_lang
844 ("artifact", "sourceLanguage", "3.24.10");
845 if (auto source_lang_jstr
846 = get_optional_property<json::string> (artifact_obj,
847 prop_source_lang))
848 sarif_source_language = source_lang_jstr->get_string ();
850 /* Create the libgdiagnostics::file. */
851 auto file = m_output_mgr.new_file (artifact_loc_uri->get_string (),
852 sarif_source_language);
854 // Set contents, if available
855 const property_spec_ref prop_contents
856 ("artifact", "contents", "3.24.8");
857 if (auto content_obj
858 = get_optional_property<json::object> (artifact_obj,
859 prop_contents))
861 // We should have an artifactContent object (§3.3)
862 const property_spec_ref prop_text
863 ("artifactContent", "text", "3.3.2");
864 if (auto text_jstr
865 = get_optional_property<json::string> (*content_obj,
866 prop_text))
867 file.set_buffered_content (text_jstr->get_string (),
868 text_jstr->get_length ());
872 /* Process a tool object (SARIF v2.1.0 section 3.18). */
874 enum status
875 sarif_replayer::handle_tool_obj (const json::object &tool_obj)
877 auto driver_obj
878 = get_required_property<json::object> (tool_obj,
879 property_spec_ref ("tool", "driver",
880 "3.18.2"));
881 if (!driver_obj)
882 return status::err_invalid_sarif;
884 const property_spec_ref name_prop ("toolComponent", "name", "3.19.8");
885 if (auto name_jstr = get_optional_property<json::string> (*driver_obj,
886 name_prop))
887 m_output_mgr.set_tool_name (name_jstr->get_string ());
889 const property_spec_ref full_name_prop
890 ("toolComponent", "fullName", "3.19.9");
891 if (auto name_jstr = get_optional_property<json::string> (*driver_obj,
892 full_name_prop))
893 m_output_mgr.set_full_name (name_jstr->get_string ());
895 const property_spec_ref version_prop ("toolComponent", "version", "3.19.13");
896 if (auto name_jstr = get_optional_property<json::string> (*driver_obj,
897 version_prop))
898 m_output_mgr.set_version_string (name_jstr->get_string ());
900 const property_spec_ref
901 info_uri_prop ("toolComponent", "informationUri", "3.19.17");
902 if (auto name_jstr = get_optional_property<json::string> (*driver_obj,
903 info_uri_prop))
904 m_output_mgr.set_version_url (name_jstr->get_string ());
906 return status::ok;
909 /* Compare the value of JSON_STR to the values in VALUE_ARR.
910 If found, return it in the result's m_val.
911 Otherwise, complain using PROP and return status::invalid_sarif
912 in results's m_err. */
914 template <typename ValueType>
915 json::result<ValueType, enum status>
916 sarif_replayer::
917 get_value_from_json_string (const json::string &json_str,
918 const property_spec_ref &prop,
919 const string_property_value<ValueType> *value_arr,
920 size_t num_values)
922 const char *str = json_str.get_string ();
923 for (size_t i = 0; i < num_values; i++)
924 if (strcmp (str, value_arr[i].m_string) == 0)
925 return value_arr[i].m_value;
926 return report_invalid_sarif (json_str, prop,
927 "unrecognized value for %qs: %qs",
928 prop.get_property_name (),
929 str);
932 const property_spec_ref prop_result_level ("result", "level", "3.27.10");
934 /* Handle a value for result's "level" property (§3.27.10).
935 Limitation: doesn't yet support "none". */
936 json::result<enum diagnostic_level, enum status>
937 sarif_replayer::get_level_from_level_str (const json::string &level_str)
939 if (strcmp (level_str.get_string (), "none") == 0)
940 return report_unhandled_sarif (level_str, prop_result_level,
941 "unable to handle value for %qs: %qs",
942 prop_result_level.get_property_name (),
943 level_str.get_string ());
945 const string_property_value<enum diagnostic_level> level_values[]
946 = { {"warning",
947 DIAGNOSTIC_LEVEL_WARNING},
948 {"error",
949 DIAGNOSTIC_LEVEL_ERROR},
950 {"note",
951 DIAGNOSTIC_LEVEL_NOTE} };
952 return get_value_from_json_string<enum diagnostic_level>
953 (level_str,
954 prop_result_level,
955 level_values, ARRAY_SIZE (level_values));
958 /* Process a result object (SARIF v2.1.0 section 3.27).
959 Known limitations:
960 - doesn't yet handle "ruleIndex" property (§3.27.6)
961 - doesn't yet handle "taxa" property (§3.27.8)
962 - handling of "level" property (§3.27.10) doesn't yet support the
963 full logic for when "level" is absent.
964 - doesn't yet handle "relatedLocations" property (§3.27.22)
965 - doesn't yet handle "fixes" property (§3.27.30)
966 - doesn't yet support multithreaded flows (§3.36.3)
969 #define PROP_result_ruleId \
970 property_spec_ref ("result", "ruleId", "3.27.5")
972 #define PROP_result_message \
973 property_spec_ref ("result", "message", "3.27.11")
975 enum status
976 sarif_replayer::handle_result_obj (const json::object &result_obj,
977 const json::object &tool_obj)
979 const json::object *rule_obj = nullptr;
980 const json::object *tool_component_obj = nullptr;
981 const json::string *rule_id
982 = get_optional_property<json::string> (result_obj, PROP_result_ruleId);
983 if (rule_id)
985 rule_obj = lookup_rule_by_id_in_tool (rule_id->get_string (),
986 tool_obj,
987 tool_component_obj);
988 // TODO: error handling
991 enum diagnostic_level level = DIAGNOSTIC_LEVEL_WARNING;
992 if (auto level_str
993 = get_optional_property<json::string> (result_obj,
994 prop_result_level))
996 auto result = get_level_from_level_str (*level_str);
997 if (result.m_err != status::ok)
998 return result.m_err;
999 level = result.m_val;
1002 // §3.27.11 "message" property
1003 label_text text;
1004 if (auto message_obj
1005 = get_optional_property<json::object> (result_obj, PROP_result_message))
1006 text = make_plain_text_within_result_message (nullptr, // TODO: tool_component_obj,
1007 *message_obj,
1008 rule_obj);
1009 if (!text.get ())
1010 return status::err_invalid_sarif;
1012 // §3.27.12 "locations" property
1013 libgdiagnostics::physical_location physical_loc;
1014 libgdiagnostics::logical_location logical_loc;
1015 const property_spec_ref locations_prop ("result", "locations", "3.27.12");
1016 const json::array *locations_arr
1017 = get_required_property<json::array> (result_obj, locations_prop);
1018 if (!locations_arr)
1019 return status::err_invalid_sarif;
1020 if (locations_arr->length () > 0)
1022 /* Only look at the first, if there's more than one. */
1023 // location objects (§3.28)
1024 const json::object *location_obj
1025 = require_object_for_element (*locations_arr->get (0), locations_prop);
1026 if (!location_obj)
1027 return status::err_invalid_sarif;
1028 enum status s = handle_location_object (*location_obj,
1029 physical_loc,
1030 logical_loc);
1031 if (s != status::ok)
1032 return s;
1035 // §3.27.18 "codeFlows" property
1036 libgdiagnostics::execution_path path;
1037 const property_spec_ref code_flows ("result", "codeFlows", "3.27.18");
1038 if (auto code_flows_arr = get_optional_property<json::array> (result_obj,
1039 code_flows))
1041 // TODO: what if more than one?
1042 if (code_flows_arr->length () == 1)
1044 const json::object *code_flow_obj
1045 = require_object_for_element (*code_flows_arr->get (0), code_flows);
1046 if (!code_flow_obj)
1047 return status::err_invalid_sarif;
1049 const property_spec_ref prop_thread_flows
1050 ("result", "threadFlows", "3.36.3");
1051 if (auto thread_flows_arr
1052 = get_optional_property<json::array> (*code_flow_obj,
1053 prop_thread_flows))
1055 if (thread_flows_arr->length () == 1)
1057 const json::object *thread_flow_obj
1058 = require_object_for_element (*thread_flows_arr->get (0),
1059 prop_thread_flows);
1060 if (!thread_flow_obj)
1061 return status::err_invalid_sarif;
1062 handle_thread_flow_object (*thread_flow_obj, path);
1068 libgdiagnostics::group g (m_output_mgr);
1069 auto err (m_output_mgr.begin_diagnostic (level));
1070 if (rule_id)
1072 const char *url = nullptr;
1073 if (rule_obj)
1075 /* rule_obj should be a reportingDescriptor object (3.49).
1076 Get any §3.49.12 helpUri property. */
1077 const property_spec_ref prop_help_uri
1078 ("reportingDescriptor", "helpUri", "3.49.12");
1079 if (auto url_val = get_optional_property<json::string>(*rule_obj,
1080 prop_help_uri))
1081 url = url_val->get_string ();
1083 err.add_rule (rule_id->get_string (), url);
1085 err.set_location (physical_loc);
1086 err.set_logical_location (logical_loc);
1087 if (path.m_inner)
1088 err.take_execution_path (std::move (path));
1089 err.finish ("%s", text.get ());
1091 // §3.27.22 relatedLocations property
1092 const property_spec_ref prop_related_locations
1093 ("result", "relatedLocations", "3.27.22");
1094 if (auto related_locations_arr
1095 = get_optional_property<json::array> (result_obj,
1096 prop_related_locations))
1098 for (auto rel_loc : *related_locations_arr)
1100 libgdiagnostics::physical_location physical_loc;
1101 libgdiagnostics::logical_location logical_loc;
1102 const json::object *location_obj
1103 = require_object_for_element (*rel_loc,
1104 prop_related_locations);
1105 if (!location_obj)
1106 return status::err_invalid_sarif;
1107 enum status s = handle_location_object (*location_obj,
1108 physical_loc,
1109 logical_loc);
1110 if (s != status::ok)
1111 return s;
1113 // §3.28.5 message property
1114 const property_spec_ref prop_message
1115 ("location", "message", "3.28.5");
1116 if (auto message_obj
1117 = get_optional_property<json::object> (*location_obj,
1118 prop_message))
1120 /* Treat related locations with a message as a "note". */
1121 label_text text
1122 (make_plain_text_within_result_message
1123 (tool_component_obj,
1124 *message_obj,
1125 rule_obj));
1126 if (!text.get ())
1127 return status::err_invalid_sarif;
1128 auto note (m_output_mgr.begin_diagnostic (DIAGNOSTIC_LEVEL_NOTE));
1129 note.set_location (physical_loc);
1130 note.set_logical_location (logical_loc);
1131 note.finish ("%s", text.get ());
1136 return status::ok;
1140 /* If ITER_SRC starts with a placeholder as per §3.11.5, advance ITER_SRC
1141 to immediately beyond the placeholder, write to *OUT_ARG_IDX, and
1142 return true.
1144 Otherwise, leave ITER_SRC untouched and return false. */
1146 static bool
1147 maybe_consume_placeholder (const char *&iter_src, unsigned *out_arg_idx)
1149 if (*iter_src != '{')
1150 return false;
1151 const char *first_digit = iter_src + 1;
1152 const char *iter_digit = first_digit;
1153 while (char ch = *iter_digit)
1154 switch (ch)
1156 default:
1157 return false;
1159 case '}':
1160 if (iter_digit == first_digit)
1162 /* No digits, we simply have "{}" which is not a placeholder
1163 (and malformed: the braces should have been escaped). */
1164 return false;
1166 *out_arg_idx = atoi (first_digit);
1167 iter_src = iter_digit + 1;
1168 return true;
1170 case '0': case '1': case '2': case '3': case '4':
1171 case '5': case '6': case '7': case '8': case '9':
1172 /* TODO: what about multiple leading zeroes? */
1173 iter_digit++;
1174 continue;
1176 return false;
1179 struct embedded_link
1181 std::string text;
1182 std::string destination;
1185 /* If ITER_SRC starts with an embedded link as per §3.11.6, advance ITER_SRC
1186 to immediately beyond the link, and return the link.
1188 Otherwise, leave ITER_SRC untouched and return nullptr. */
1190 static std::unique_ptr<embedded_link>
1191 maybe_consume_embedded_link (const char *&iter_src)
1193 if (*iter_src != '[')
1194 return nullptr;
1196 /* This *might* be an embedded link.
1197 See §3.11.6 ("Messages with embedded links") and
1198 https://github.com/oasis-tcs/sarif-spec/issues/657 */
1200 /* embedded link = "[", link text, "](", link destination, ")"; */
1202 embedded_link result;
1204 /* Try to get the link text. */
1205 const char *iter = iter_src + 1;
1206 while (char ch = *(iter++))
1208 if (ch == '\\')
1210 char next_ch = *iter;
1211 switch (next_ch)
1213 case '\\':
1214 case '[':
1215 case ']':
1216 /* escaped link character = "\" | "[" | "]"; */
1217 result.text += next_ch;
1218 iter++;
1219 continue;
1221 default:
1222 /* Malformed link text; assume this is not an
1223 embedded link. */
1224 return nullptr;
1227 else if (ch == ']')
1228 /* End of link text. */
1229 break;
1230 else
1231 result.text += ch;
1234 if (*iter++ != '(')
1235 return nullptr;
1237 /* Try to get the link destination. */
1238 while (1)
1240 char ch = *(iter++);
1241 if (ch == '\0')
1243 /* String ended before terminating ')'.
1244 Assume this is not an embedded link. */
1245 return nullptr;
1247 else if (ch == ')')
1248 /* Terminator. */
1249 break;
1250 else
1251 result.destination += ch;
1254 iter_src = iter;
1255 return ::make_unique<embedded_link> (std::move (result));
1258 /* Lookup the plain text string within a result.message (§3.27.11),
1259 and substitute for any placeholders (§3.11.5) and handle any
1260 embedded links (§3.11.6).
1262 Limitations:
1263 - we don't preserve destinations within embedded links
1265 MESSAGE_OBJ is "theMessage"
1266 RULE_OBJ is "theRule". */
1268 label_text
1269 sarif_replayer::
1270 make_plain_text_within_result_message (const json::object *tool_component_obj,
1271 const json::object &message_obj,
1272 const json::object *rule_obj)
1274 const char *original_text
1275 = lookup_plain_text_within_result_message (tool_component_obj,
1276 message_obj,
1277 rule_obj);
1278 if (!original_text)
1279 return label_text::borrow (nullptr);
1281 /* Look up any arguments for substituting into placeholders. */
1282 const property_spec_ref arguments_prop ("message", "arguments", "3.11.11");
1283 const json::array *arguments
1284 = get_optional_property<json::array> (message_obj, arguments_prop);
1286 /* Duplicate original_text, substituting any placeholders. */
1287 std::string accum;
1289 const char *iter_src = original_text;
1290 while (char ch = *iter_src)
1292 unsigned arg_idx;
1293 if (maybe_consume_placeholder (iter_src, &arg_idx))
1295 if (!arguments)
1297 report_invalid_sarif
1298 (message_obj, arguments_prop,
1299 "message string contains placeholder %<{%i}%>"
1300 " but message object has no %qs property",
1301 (int)arg_idx,
1302 arguments_prop.get_property_name ());
1303 return label_text::borrow (nullptr);
1305 if (arg_idx >= arguments->length ())
1307 report_invalid_sarif
1308 (message_obj, arguments_prop,
1309 "not enough strings in %qs array for"
1310 " placeholder %<{%i}%>",
1311 arguments_prop.get_property_name (),
1312 (int)arg_idx);
1313 // TODO: might be nice to add a note showing the args
1314 return label_text::borrow (nullptr);
1316 auto replacement_jstr
1317 = require_string (*arguments->get (arg_idx), arguments_prop);
1318 if (!replacement_jstr)
1319 return label_text::borrow (nullptr);
1320 accum += replacement_jstr->get_string ();
1322 else if (ch == '{' || ch == '}')
1324 /* '{' and '}' are escaped by repeating them. */
1325 if (iter_src[1] == ch)
1327 accum += ch;
1328 iter_src += 2;
1330 else
1332 report_invalid_sarif (message_obj, arguments_prop,
1333 "unescaped '%c' within message string",
1334 ch);
1335 return label_text::borrow (nullptr);
1338 else if (auto link = maybe_consume_embedded_link (iter_src))
1340 accum += link->text;
1341 /* TODO: use the destination. */
1342 /* TODO: potentially could try to convert
1343 intra-sarif links into event ids. */
1345 else
1347 accum += ch;
1348 iter_src++;
1352 return label_text::take (xstrdup (accum.c_str ()));
1355 /* Handle a value that should be a multiformatMessageString object (§3.12).
1356 Complain using prop if MFMS_VAL is not an object.
1357 Return get the "text" value (or nullptr, and complain). */
1359 const char *
1360 sarif_replayer::get_plain_text_from_mfms (json::value &mfms_val,
1361 const property_spec_ref &prop)
1363 auto mfms_obj = require_object (mfms_val, prop);
1364 if (!mfms_obj)
1365 return nullptr;
1367 const property_spec_ref text_prop
1368 ("multiformatMessageString", "text", "3.12.3");
1369 auto text_jstr = get_required_property<json::string> (*mfms_obj, text_prop);
1370 if (!text_jstr)
1371 return nullptr;
1372 return text_jstr->get_string ();
1375 #define PROP_message_text \
1376 property_spec_ref ("message", "text", "3.11.8")
1378 #define PROP_message_id \
1379 property_spec_ref ("message", "id", "3.11.10")
1381 /* Implement the message string lookup algorithm from
1382 SARIF v2.1.0 section 3.11.7, for the case where theMessage
1383 is the value of result.message (§3.27.11).
1385 MESSAGE_OBJ is "theMessage"
1386 RULE_OBJ is "theRule". */
1388 const char *
1389 sarif_replayer::
1390 lookup_plain_text_within_result_message (const json::object *tool_component_obj,
1391 const json::object &message_obj,
1392 const json::object *rule_obj)
1394 // rule_obj can be NULL
1396 /* IF theMessage.text is present and the desired language is theRun.language THEN
1397 Use the text or markdown property of theMessage as appropriate. */
1398 if (const json::string *str
1399 = get_optional_property<json::string> (message_obj, PROP_message_text))
1400 // TODO: check language
1401 return str->get_string ();
1403 if (rule_obj)
1404 if (auto message_id_jstr
1405 = get_optional_property<json::string> (message_obj, PROP_message_id))
1407 const char *message_id = message_id_jstr->get_string ();
1408 const property_spec_ref message_strings
1409 ("reportingDescriptor", "messageStrings", "3.49.11");
1410 if (auto message_strings_obj
1411 = get_optional_property<json::object> (*rule_obj,
1412 message_strings))
1413 if (json::value *mfms = message_strings_obj->get (message_id))
1414 return get_plain_text_from_mfms (*mfms, message_strings);
1416 /* Look up by theMessage.id within theComponent.globalMessageStrings
1417 (§3.19.22). */
1418 if (tool_component_obj)
1420 const property_spec_ref prop_gms
1421 ("toolComponent", "globalMessageStrings", "3.19.22");
1422 if (auto global_message_strings
1423 = get_optional_property<json::object> (*tool_component_obj,
1424 prop_gms))
1425 if (auto mfms = global_message_strings->get (message_id))
1426 return get_plain_text_from_mfms (*mfms, prop_gms);
1430 /* Failure. */
1431 report_invalid_sarif (message_obj, spec_ref ("3.11.7"),
1432 "could not find string for %<message%> object");
1433 return nullptr;
1436 /* Populate OUT for THREAD_FLOW_OBJ, a
1437 SARIF threadFlow object (section 3.37). */
1439 enum status
1440 sarif_replayer::handle_thread_flow_object (const json::object &thread_flow_obj,
1441 libgdiagnostics::execution_path &out)
1443 const property_spec_ref locations ("threadFlow", "locations", "3.37.6");
1444 const json::array *locations_arr
1445 = get_required_property<json::array> (thread_flow_obj, locations);
1446 if (!locations_arr)
1447 return status::err_invalid_sarif;
1449 out = m_output_mgr.new_execution_path ();
1450 for (auto location : *locations_arr)
1452 /* threadFlowLocation object (§3.38). */
1453 const json::object *tflow_loc_obj
1454 = require_object_for_element (*location, locations);
1455 if (!tflow_loc_obj)
1456 return status::err_invalid_sarif;
1457 handle_thread_flow_location_object (*tflow_loc_obj, out);
1460 return status::ok;
1463 /* "threadFlowLocation" object (§3.38).
1464 Attempt to add an event for TFLOW_LOC_OBJ to PATH. */
1466 enum status
1467 sarif_replayer::
1468 handle_thread_flow_location_object (const json::object &tflow_loc_obj,
1469 libgdiagnostics::execution_path &path)
1471 libgdiagnostics::physical_location physical_loc;
1472 libgdiagnostics::logical_location logical_loc;
1473 label_text message;
1474 int stack_depth = 0;
1476 const property_spec_ref location_prop
1477 ("threadFlowLocation", "location", "3.38.3");
1478 if (auto location_obj = get_optional_property<json::object> (tflow_loc_obj,
1479 location_prop))
1481 /* location object (§3.28). */
1482 enum status s
1483 = handle_location_object (*location_obj, physical_loc, logical_loc);
1484 if (s != status::ok)
1485 return s;
1487 /* Get any message from here. */
1488 const property_spec_ref location_message
1489 ("location", "message", "3.28.5");
1490 if (auto message_obj
1491 = get_optional_property<json::object> (*location_obj,
1492 location_message))
1494 message = make_plain_text_within_result_message
1495 (nullptr,
1496 *message_obj,
1497 nullptr/* TODO. */);
1501 // §3.38.8 "kinds" property
1502 const property_spec_ref kinds ("threadFlowLocation", "kinds", "3.38.8");
1503 if (auto kinds_arr
1504 = get_optional_property<json::array> (tflow_loc_obj, kinds))
1506 std::vector<const char *> kind_strs;
1507 for (auto iter : *kinds_arr)
1509 const json::string *kind_str = dyn_cast <const json::string *> (iter);
1510 if (!kind_str)
1513 kind_strs.push_back (kind_str->get_string ());
1514 // TODO: handle meaning?
1515 /* TOOD: probably just want to add sarif kinds to
1516 the libgdiagnostics event, and have libgdiagnostics
1517 turn that back into a "meaning". */
1521 /* nestingLevel property (§3.38.10). */
1522 const property_spec_ref nesting_level
1523 ("threadFlowLocation", "nestingLevel", "3.38.10");
1524 if (auto nesting_level_jv
1525 = get_optional_property<json::integer_number> (tflow_loc_obj,
1526 nesting_level))
1528 stack_depth = nesting_level_jv->get ();
1529 if (stack_depth < 0)
1531 return report_invalid_sarif (tflow_loc_obj, nesting_level,
1532 "expected a non-negative integer");
1536 if (message.get ())
1537 path.add_event (physical_loc,
1538 logical_loc,
1539 stack_depth,
1540 "%s", message.get ());
1541 else
1542 path.add_event (physical_loc,
1543 logical_loc,
1544 stack_depth,
1545 "");
1547 return status::ok;
1550 /* Handle LOCATION_OBJ, a "location" (§3.28). */
1552 enum status
1553 sarif_replayer::
1554 handle_location_object (const json::object &location_obj,
1555 libgdiagnostics::physical_location &out_physical_loc,
1556 libgdiagnostics::logical_location &out_logical_loc)
1558 // §3.28.3 "physicalLocation" property
1560 const property_spec_ref physical_location_prop
1561 ("location", "physicalLocation", "3.28.3");
1562 if (const json::object *phys_loc_obj
1563 = get_optional_property<json::object> (location_obj,
1564 physical_location_prop))
1566 enum status s = handle_physical_location_object (*phys_loc_obj,
1567 out_physical_loc);
1568 if (s!= status::ok)
1569 return s;
1573 // §3.28.4 "logicalLocations" property
1575 const property_spec_ref logical_locations_prop
1576 ("location", "logicalLocations", "3.28.4");
1577 if (const json::array *logical_loc_arr
1578 = get_optional_property<json::array> (location_obj,
1579 logical_locations_prop))
1580 if (logical_loc_arr->length () > 0)
1582 /* Only look at the first, if there's more than one. */
1583 const json::object *logical_loc_obj
1584 = require_object_for_element (*logical_loc_arr->get (0),
1585 logical_locations_prop);
1586 if (!logical_loc_obj)
1587 return status::err_invalid_sarif;
1588 enum status s = handle_logical_location_object (*logical_loc_obj,
1589 out_logical_loc);
1590 if (s != status::ok)
1591 return s;
1595 return status::ok;
1598 /* Handle PHYS_LOC_OBJ, a "physicalLocation" object (§3.29).
1599 Limitations:
1600 - we don't yet support the "contextRegion" property (§3.29.5) */
1602 enum status
1603 sarif_replayer::
1604 handle_physical_location_object (const json::object &phys_loc_obj,
1605 libgdiagnostics::physical_location &out)
1607 libgdiagnostics::file artifact_file;
1609 // §3.29.3 "artifactLocation" property
1610 const property_spec_ref artifact_location_prop
1611 ("physicalLocation", "artifactLocation", "3.29.3");
1612 if (const json::object *artifact_loc_obj
1613 = get_optional_property<json::object> (phys_loc_obj,
1614 artifact_location_prop))
1616 enum status s = handle_artifact_location_object (*artifact_loc_obj,
1617 artifact_file);
1618 if (s != status::ok)
1619 return s;
1622 // §3.29.6 "address" property
1623 const property_spec_ref artifact_address_prop
1624 ("physicalLocation", "address", "3.29.6");
1626 if (!artifact_file.m_inner)
1628 const object_spec_ref constraints ("physicalLocation", "3.29.2");
1629 return report_invalid_sarif_at_least_one_of (phys_loc_obj,
1630 constraints,
1631 artifact_address_prop,
1632 artifact_location_prop);
1635 //3.29.4 region property
1636 const property_spec_ref region_prop ("physicalLocation", "region", "3.29.4");
1637 if (const json::object *region_obj
1638 = get_optional_property<json::object> (phys_loc_obj, region_prop))
1640 enum status s
1641 = handle_region_object (*region_obj, artifact_file, out);
1642 if (s != status::ok)
1643 return s;
1644 // TODO:
1647 return status::ok;
1650 /* Handle ARTIFACT_LOC, an "artifactLocation" object (§3.4). */
1652 enum status
1653 sarif_replayer::handle_artifact_location_object (const json::object &artifact_loc,
1654 libgdiagnostics::file &out)
1656 // §3.4.3 "uri" property
1657 const property_spec_ref uri_prop ("artifactLocation", "uri", "3.4.3");
1658 auto uri = get_optional_property<json::string> (artifact_loc, uri_prop);
1660 // §3.4.5 "index" property
1661 const property_spec_ref index_prop ("artifactLocation", "index", "3.4.5");
1662 auto index = get_optional_property<json::integer_number> (artifact_loc,
1663 index_prop);
1664 if (uri == nullptr && index == nullptr)
1666 object_spec_ref constraints ("artifactLocation", "3.4.2");
1667 return report_invalid_sarif_at_least_one_of (artifact_loc,
1668 constraints,
1669 uri_prop,
1670 index_prop);
1673 if (uri)
1675 // TODO: source language
1676 out = m_output_mgr.new_file (uri->get_string (), nullptr);
1677 return status::ok;
1680 return status::ok;
1683 /* Handle a "region" object (§3.30) within FILE, writing to OUT. */
1685 enum status
1686 sarif_replayer::
1687 handle_region_object (const json::object &region_obj,
1688 libgdiagnostics::file file,
1689 libgdiagnostics::physical_location &out)
1691 gcc_assert (file.m_inner);
1693 // §3.30.5 "startLine" property
1694 const property_spec_ref start_line_prop ("region", "startLine", "3.30.5");
1695 libgdiagnostics::physical_location start;
1696 libgdiagnostics::physical_location end;
1697 if (auto start_line_jnum
1698 = get_optional_property<json::integer_number> (region_obj,
1699 start_line_prop))
1701 /* Text region defined by line/column properties. */
1702 const property_spec_ref start_column_prop
1703 ("region", "startColumn", "3.30.6");
1704 if (auto start_column_jnum
1705 = get_optional_property<json::integer_number> (region_obj,
1706 start_column_prop))
1708 start = m_output_mgr.new_location_from_file_line_column
1709 (file, start_line_jnum->get (), start_column_jnum->get ());
1711 else
1712 start = m_output_mgr.new_location_from_file_and_line
1713 (file, start_line_jnum->get ());
1715 int end_line = start_line_jnum->get ();
1716 const property_spec_ref end_line_prop ("region", "endLine", "3.30.7");
1717 if (auto end_line_jnum
1718 = get_optional_property<json::integer_number> (region_obj,
1719 end_line_prop))
1720 end_line = end_line_jnum->get ();
1722 const property_spec_ref end_column_prop ("region", "endColumn", "3.30.8");
1723 if (auto end_column_jnum
1724 = get_optional_property<json::integer_number> (region_obj,
1725 end_column_prop))
1727 /* SARIF's endColumn is 1 beyond the final column in the region,
1728 whereas GCC's end columns are inclusive. */
1729 end = m_output_mgr.new_location_from_file_line_column
1730 (file, end_line, end_column_jnum->get ());
1732 else
1734 // missing "endColumn" means the whole of the rest of the row
1735 end = m_output_mgr.new_location_from_file_and_line
1736 (file, end_line);
1739 out = m_output_mgr.new_location_from_range (start, start, end);
1742 return status::ok;
1745 /* Handle a "logicalLocation" object (§3.33), using it to populate OUT.
1746 Known limitations:
1747 - doesn't yet handle "parentIndex" property (§3.33.8)
1750 enum status
1751 sarif_replayer::
1752 handle_logical_location_object (const json::object &logical_loc_obj,
1753 libgdiagnostics::logical_location &out)
1755 const property_spec_ref name_prop ("logicalLocation", "name", "3.33.4");
1756 const char *short_name = nullptr;
1757 if (auto name_jstr = get_optional_property<json::string> (logical_loc_obj,
1758 name_prop))
1759 short_name = name_jstr->get_string ();
1761 const property_spec_ref fqname_prop
1762 ("logicalLocation", "fullyQualifiedName", "3.33.5");
1763 const char *fully_qualified_name = nullptr;
1764 if (auto fully_qualified_name_jstr
1765 = get_optional_property<json::string> (logical_loc_obj, fqname_prop))
1766 fully_qualified_name = fully_qualified_name_jstr->get_string ();
1768 const property_spec_ref decorated_name_prop
1769 ("logicalLocation", "decoratedName", "3.33.6");
1770 const char *decorated_name = nullptr;
1771 if (auto decorated_name_jstr
1772 = get_optional_property<json::string> (logical_loc_obj,
1773 decorated_name_prop))
1774 decorated_name = decorated_name_jstr->get_string ();
1776 // §3.33.7 "kind" property
1777 const property_spec_ref kind_prop ("logicalLocation", "kind", "3.33.7");
1778 enum diagnostic_logical_location_kind_t kind
1779 = DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION;
1780 if (auto kind_str = get_optional_property<json::string> (logical_loc_obj,
1781 kind_prop))
1783 const string_property_value<enum diagnostic_logical_location_kind_t>
1784 kind_values[]
1786 { "function",
1787 DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION },
1788 { "member",
1789 DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER },
1790 { "module",
1791 DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE },
1792 { "namespace",
1793 DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE },
1794 { "type",
1795 DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE },
1796 { "returnType",
1797 DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE },
1798 { "parameter",
1799 DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER },
1800 { "variable",
1801 DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE } };
1802 auto result
1803 = get_value_from_json_string<enum diagnostic_logical_location_kind_t>
1804 (*kind_str, kind_prop, kind_values, ARRAY_SIZE (kind_values));
1805 if (result.m_err != status::ok)
1806 return result.m_err;
1807 kind = result.m_val;
1810 libgdiagnostics::logical_location parent;
1811 out = m_output_mgr.new_logical_location (kind,
1812 parent,
1813 short_name,
1814 fully_qualified_name,
1815 decorated_name);
1817 return status::ok;
1820 // 3.52.3 reportingDescriptor lookup
1821 // "For an example of the interaction between ruleId and rule.id, see §3.52.4."
1823 const json::object *
1824 sarif_replayer::
1825 lookup_rule_by_id_in_tool (const char *rule_id,
1826 const json::object &tool_obj,
1827 const json::object *&tool_component_obj)
1829 auto driver_obj
1830 = get_required_property<json::object> (tool_obj,
1831 property_spec_ref ("tool", "driver",
1832 "3.18.2"));
1833 if (!driver_obj)
1834 return nullptr;
1836 if (auto rule_obj = lookup_rule_by_id_in_component (rule_id, *driver_obj))
1838 tool_component_obj = driver_obj;
1839 return rule_obj;
1842 // TODO: also handle extensions
1844 return NULL;
1847 const json::object *
1848 sarif_replayer::
1849 lookup_rule_by_id_in_component (const char *rule_id,
1850 const json::object &tool_component_obj)
1852 const property_spec_ref rules ("toolComponent", "rules", "3.18.2");
1854 auto rules_arr
1855 = get_optional_property<json::array> (tool_component_obj, rules);
1856 if (!rules_arr)
1857 return nullptr;
1859 for (auto element : *rules_arr)
1861 const json::object *reporting_desc_obj
1862 = require_object_for_element (*element, rules);
1864 /* reportingDescriptor objects (§3.49). */
1865 const property_spec_ref id ("reportingDescriptor", "id", "3.49.3");
1866 auto desc_id_jstr
1867 = get_required_property<json::string> (*reporting_desc_obj, id);
1868 if (!desc_id_jstr)
1869 return nullptr;
1871 if (!strcmp (rule_id, desc_id_jstr->get_string ()))
1872 return reporting_desc_obj;
1875 /* Not found. */
1876 return nullptr;
1879 } // anonymous namespace
1881 /* Error-checking at the API boundary. */
1883 #define FAIL_IF_NULL(PTR_ARG) \
1884 do { \
1885 GCC_DIAGNOSTIC_PUSH_IGNORED(-Wnonnull-compare) \
1886 if (!(PTR_ARG)) { \
1887 fprintf (stderr, "%s: %s must be non-NULL\n", \
1888 __func__, #PTR_ARG); \
1889 abort (); \
1891 GCC_DIAGNOSTIC_POP \
1892 } while (0)
1894 /* Public entrypoint. */
1897 sarif_replay_path (const char *sarif_file,
1898 diagnostic_manager *output_manager,
1899 diagnostic_manager *control_manager,
1900 const replay_options *options)
1902 FAIL_IF_NULL (sarif_file);
1903 FAIL_IF_NULL (output_manager);
1904 FAIL_IF_NULL (control_manager);
1905 FAIL_IF_NULL (options);
1907 sarif_replayer r (libgdiagnostics::manager (output_manager, false),
1908 libgdiagnostics::manager (control_manager, false));
1909 return (int)r.replay_file (sarif_file, *options);