1 /* A library for re-emitting diagnostics saved in SARIF form
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
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
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/>. */
23 #define INCLUDE_VECTOR
25 #define INCLUDE_STRING
27 #include "coretypes.h"
28 #include "make-unique.h"
29 #include "libgdiagnostics++.h"
30 #include "json-parsing.h"
32 #include "sarif-spec-urls.def"
33 #include "libsarifreplay.h"
34 #include "label-text.h"
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");
47 char *errmsg
= xstrerror (errno
);
48 auto err (mgr
.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR
));
49 err
.finish ("cannot open %qs: %s", path
, errmsg
);
53 /* Read content, allocating a buffer for it. */
54 auto result
= ::make_unique
<std::vector
<char>> ();
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
);
75 char *errmsg
= xstrerror (errno
);
76 auto err (mgr
.begin_diagnostic (DIAGNOSTIC_LEVEL_ERROR
));
77 err
.finish ("error reading from %qs: %s", path
, errmsg
);
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
,
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
);
119 /* A reference to the SARIF specification. */
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
);
140 return xasprintf ("%s#%s", sarif_spec_base_url
, anchor
);
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
;
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
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
; }
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
182 property_spec_ref (const char *obj_name
,
183 const char *property_name
,
185 : object_spec_ref (obj_name
, section
),
186 m_property_name (property_name
)
189 const char *get_property_name () const { return m_property_name
; }
192 const char *m_property_name
;
195 template <typename ValueType
>
196 struct string_property_value
198 const char *m_string
;
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
);
218 class replayer_location_map
: public json::location_map
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 ());
236 std::map
<const json::value
*, range
> m_map_jv_to_range
;
239 enum status
emit_sarif_as_diagnostics (const json::value
&jv
);
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)
251 handle_artifact_location_object (const json::object
&artifact_loc
,
252 libgdiagnostics::file
&out
);
254 // Message string lookup algorithm (§3.11.7)
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).
262 get_plain_text_from_mfms (json::value
&mfms_val
,
263 const property_spec_ref
&prop
);
265 // "run" object (§3.14)
267 handle_run_obj (const json::object
&run_obj
);
269 // "tool" object (§3.18)
271 handle_tool_obj (const json::object
&tool_obj
);
273 // "artifact" object (§3.24). */
275 handle_artifact_obj (const json::object
&artifact_obj
);
277 // "result" object (§3.27)
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)
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)
292 handle_physical_location_object (const json::object
&phys_loc_obj
,
293 libgdiagnostics::physical_location
&out
);
295 // "region" object (§3.30)
297 handle_region_object (const json::object
®ion_obj
,
298 libgdiagnostics::file file
,
299 libgdiagnostics::physical_location
&out
);
301 // "logicalLocation" object (§3.33)
303 handle_logical_location_object (const json::object
&logical_loc_obj
,
304 libgdiagnostics::logical_location
&out
);
306 // "threadFlow" object (§3.37)
308 handle_thread_flow_object (const json::object
&thread_flow_obj
,
309 libgdiagnostics::execution_path
&out
);
311 // "threadFlowLocation" object (§3.38)
313 handle_thread_flow_location_object (const json::object
&tflow_loc_obj
,
314 libgdiagnostics::execution_path
&out
);
316 // reportingDescriptor lookup (§3.52.3)
318 lookup_rule_by_id_in_tool (const char *rule_id
,
319 const json::object
&tool_obj
,
320 const json::object
*&tool_component_obj
);
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. */
332 report_invalid_sarif (const json::value
&jv
,
334 const char *gmsgid
, ...)
335 LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (4, 5)
338 va_start (ap
, gmsgid
);
339 report_problem (jv
, ref
, gmsgid
, &ap
, DIAGNOSTIC_LEVEL_ERROR
);
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. */
348 report_unhandled_sarif (const json::value
&jv
,
350 const char *gmsgid
, ...)
351 LIBGDIAGNOSTICS_PARAM_GCC_FORMAT_STRING (4, 5)
354 va_start (ap
, gmsgid
);
355 report_problem (jv
, ref
, gmsgid
, &ap
, DIAGNOSTIC_LEVEL_SORRY
);
357 return status::err_unhandled_sarif
;
361 report_problem (const json::value
&jv
,
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
);
381 = make_physical_location (m_control_mgr
,
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. */
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. */
411 require_object (const json::value
&val
, const property_spec_ref
&ref
)
413 const json::object
*obj
= dyn_cast
<const json::object
*> (&val
);
416 report_invalid_sarif (val
, ref
, "expected %s.%s to be an object",
417 ref
.get_obj_name (), ref
.get_property_name ());
423 /* Require VAL to be a json::string
424 If successful, return it as an string.
425 Otherwise, complain using REF and return nullptr. */
427 require_string (const json::value
&val
, const property_spec_ref
&ref
)
429 const json::string
*str
= dyn_cast
<const json::string
*> (&val
);
432 report_invalid_sarif (val
, ref
, "expected %s.%s to be an string",
433 ref
.get_obj_name (), ref
.get_property_name ());
438 /* Look for an optional property within OBJ based on REF. */
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
>
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
);
459 const JsonType
*sub
= dyn_cast
<const JsonType
*> (property_val
);
462 /* Property is wrong kind of value. */
463 report_bad_property_kind
<JsonType
> (obj
, ref
, *property_val
);
469 /* Require VAL to be a json::object.
470 Look for a property of VAL based on REF, which must be of
472 If successful, return the property's value.
473 Otherwise, complain and return nullptr. */
474 template <typename JsonType
>
476 get_required_property (const json::value
&val
,
477 const property_spec_ref
&ref
)
479 const json::object
*obj
= require_object (val
, ref
);
482 return get_required_property
<JsonType
> (*obj
, ref
);
485 /* Look for a property of VAL based on REF, which must be of
487 If successful, return the property's value.
488 Otherwise, complain and return nullptr. */
489 template <typename 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
);
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 ());
503 const JsonType
*sub
= dyn_cast
<const JsonType
*> (property_val
);
506 /* Property is wrong kind of value. */
507 report_bad_property_kind
<JsonType
> (obj
, ref
, *property_val
);
513 template <typename JsonType
>
515 report_bad_property_kind (const json::object
&obj
,
516 const property_spec_ref
&ref
,
517 const json::value
&property_val
);
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
);
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 ());
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
,
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
;
557 describe_kind (const json::value
&val
)
559 switch (val
.get_kind ())
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. */
588 report_bad_property_kind
<json::value
> (const json::object
&,
589 const property_spec_ref
&,
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
));
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
));
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
));
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
));
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
);
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
,
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")
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
);
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
);
716 return status::err_invalid_sarif
;
718 switch (runs
->get_kind ())
721 return report_invalid_sarif (*runs
, prop_runs
,
722 "expected sarifLog.runs to be"
723 " %<null%> or an array");
725 case json::JSON_NULL
:
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
);
737 return status::err_invalid_sarif
;
738 enum status s
= handle_run_obj (*run_obj
);
749 /* Process a run object (SARIF v2.1.0 section 3.14). */
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",
759 return status::err_invalid_sarif
;
761 enum status err
= handle_tool_obj (*tool_obj
);
762 if (err
!= status::ok
)
767 = get_required_property
<json::object
> (*tool_obj
,
768 property_spec_ref ("tool", "driver",
771 return status::err_invalid_sarif
;
773 const property_spec_ref
prop_artifacts ("run", "artifacts", "3.14.15");
775 = get_optional_property
<json::array
> (run_obj
, prop_artifacts
);
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
);
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
,
790 switch (results
->get_kind ())
793 return report_invalid_sarif (*results
, prop_results
,
794 "expected run.results to be"
795 " %<null%> or an array");
797 case json::JSON_NULL
:
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
);
808 return status::err_invalid_sarif
;
809 enum status s
= handle_result_obj (*result_obj
, *tool_obj
);
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. */
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
)
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
)
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
,
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");
858 = get_optional_property
<json::object
> (artifact_obj
,
861 // We should have an artifactContent object (§3.3)
862 const property_spec_ref prop_text
863 ("artifactContent", "text", "3.3.2");
865 = get_optional_property
<json::string
> (*content_obj
,
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). */
875 sarif_replayer::handle_tool_obj (const json::object
&tool_obj
)
878 = get_required_property
<json::object
> (tool_obj
,
879 property_spec_ref ("tool", "driver",
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
,
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
,
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
,
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
,
904 m_output_mgr
.set_version_url (name_jstr
->get_string ());
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
>
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
,
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 (),
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
[]
947 DIAGNOSTIC_LEVEL_WARNING
},
949 DIAGNOSTIC_LEVEL_ERROR
},
951 DIAGNOSTIC_LEVEL_NOTE
} };
952 return get_value_from_json_string
<enum diagnostic_level
>
955 level_values
, ARRAY_SIZE (level_values
));
958 /* Process a result object (SARIF v2.1.0 section 3.27).
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")
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
);
985 rule_obj
= lookup_rule_by_id_in_tool (rule_id
->get_string (),
988 // TODO: error handling
991 enum diagnostic_level level
= DIAGNOSTIC_LEVEL_WARNING
;
993 = get_optional_property
<json::string
> (result_obj
,
996 auto result
= get_level_from_level_str (*level_str
);
997 if (result
.m_err
!= status::ok
)
999 level
= result
.m_val
;
1002 // §3.27.11 "message" property
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,
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
);
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
);
1027 return status::err_invalid_sarif
;
1028 enum status s
= handle_location_object (*location_obj
,
1031 if (s
!= status::ok
)
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
,
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
);
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
,
1055 if (thread_flows_arr
->length () == 1)
1057 const json::object
*thread_flow_obj
1058 = require_object_for_element (*thread_flows_arr
->get (0),
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
));
1072 const char *url
= nullptr;
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
,
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
);
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
);
1106 return status::err_invalid_sarif
;
1107 enum status s
= handle_location_object (*location_obj
,
1110 if (s
!= status::ok
)
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
,
1120 /* Treat related locations with a message as a "note". */
1122 (make_plain_text_within_result_message
1123 (tool_component_obj
,
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 ());
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
1144 Otherwise, leave ITER_SRC untouched and return false. */
1147 maybe_consume_placeholder (const char *&iter_src
, unsigned *out_arg_idx
)
1149 if (*iter_src
!= '{')
1151 const char *first_digit
= iter_src
+ 1;
1152 const char *iter_digit
= first_digit
;
1153 while (char ch
= *iter_digit
)
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). */
1166 *out_arg_idx
= atoi (first_digit
);
1167 iter_src
= iter_digit
+ 1;
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? */
1179 struct embedded_link
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
!= '[')
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
++))
1210 char next_ch
= *iter
;
1216 /* escaped link character = "\" | "[" | "]"; */
1217 result
.text
+= next_ch
;
1222 /* Malformed link text; assume this is not an
1228 /* End of link text. */
1237 /* Try to get the link destination. */
1240 char ch
= *(iter
++);
1243 /* String ended before terminating ')'.
1244 Assume this is not an embedded link. */
1251 result
.destination
+= ch
;
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).
1263 - we don't preserve destinations within embedded links
1265 MESSAGE_OBJ is "theMessage"
1266 RULE_OBJ is "theRule". */
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
,
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. */
1289 const char *iter_src
= original_text
;
1290 while (char ch
= *iter_src
)
1293 if (maybe_consume_placeholder (iter_src
, &arg_idx
))
1297 report_invalid_sarif
1298 (message_obj
, arguments_prop
,
1299 "message string contains placeholder %<{%i}%>"
1300 " but message object has no %qs property",
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 (),
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
)
1332 report_invalid_sarif (message_obj
, arguments_prop
,
1333 "unescaped '%c' within message string",
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. */
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). */
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
);
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
);
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". */
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 ();
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
,
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
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
,
1425 if (auto mfms
= global_message_strings
->get (message_id
))
1426 return get_plain_text_from_mfms (*mfms
, prop_gms
);
1431 report_invalid_sarif (message_obj
, spec_ref ("3.11.7"),
1432 "could not find string for %<message%> object");
1436 /* Populate OUT for THREAD_FLOW_OBJ, a
1437 SARIF threadFlow object (section 3.37). */
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
);
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
);
1456 return status::err_invalid_sarif
;
1457 handle_thread_flow_location_object (*tflow_loc_obj
, out
);
1463 /* "threadFlowLocation" object (§3.38).
1464 Attempt to add an event for TFLOW_LOC_OBJ to PATH. */
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
;
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
,
1481 /* location object (§3.28). */
1483 = handle_location_object (*location_obj
, physical_loc
, logical_loc
);
1484 if (s
!= status::ok
)
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
,
1494 message
= make_plain_text_within_result_message
1497 nullptr/* TODO. */);
1501 // §3.38.8 "kinds" property
1502 const property_spec_ref
kinds ("threadFlowLocation", "kinds", "3.38.8");
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
);
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
,
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");
1537 path
.add_event (physical_loc
,
1540 "%s", message
.get ());
1542 path
.add_event (physical_loc
,
1550 /* Handle LOCATION_OBJ, a "location" (§3.28). */
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
,
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
,
1590 if (s
!= status::ok
)
1598 /* Handle PHYS_LOC_OBJ, a "physicalLocation" object (§3.29).
1600 - we don't yet support the "contextRegion" property (§3.29.5) */
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
,
1618 if (s
!= status::ok
)
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
,
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
))
1641 = handle_region_object (*region_obj
, artifact_file
, out
);
1642 if (s
!= status::ok
)
1650 /* Handle ARTIFACT_LOC, an "artifactLocation" object (§3.4). */
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
,
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
,
1675 // TODO: source language
1676 out
= m_output_mgr
.new_file (uri
->get_string (), nullptr);
1683 /* Handle a "region" object (§3.30) within FILE, writing to OUT. */
1687 handle_region_object (const json::object
®ion_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
,
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
,
1708 start
= m_output_mgr
.new_location_from_file_line_column
1709 (file
, start_line_jnum
->get (), start_column_jnum
->get ());
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
,
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
,
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 ());
1734 // missing "endColumn" means the whole of the rest of the row
1735 end
= m_output_mgr
.new_location_from_file_and_line
1739 out
= m_output_mgr
.new_location_from_range (start
, start
, end
);
1745 /* Handle a "logicalLocation" object (§3.33), using it to populate OUT.
1747 - doesn't yet handle "parentIndex" property (§3.33.8)
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
,
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
,
1783 const string_property_value
<enum diagnostic_logical_location_kind_t
>
1787 DIAGNOSTIC_LOGICAL_LOCATION_KIND_FUNCTION
},
1789 DIAGNOSTIC_LOGICAL_LOCATION_KIND_MEMBER
},
1791 DIAGNOSTIC_LOGICAL_LOCATION_KIND_MODULE
},
1793 DIAGNOSTIC_LOGICAL_LOCATION_KIND_NAMESPACE
},
1795 DIAGNOSTIC_LOGICAL_LOCATION_KIND_TYPE
},
1797 DIAGNOSTIC_LOGICAL_LOCATION_KIND_RETURN_TYPE
},
1799 DIAGNOSTIC_LOGICAL_LOCATION_KIND_PARAMETER
},
1801 DIAGNOSTIC_LOGICAL_LOCATION_KIND_VARIABLE
} };
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
,
1814 fully_qualified_name
,
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
*
1825 lookup_rule_by_id_in_tool (const char *rule_id
,
1826 const json::object
&tool_obj
,
1827 const json::object
*&tool_component_obj
)
1830 = get_required_property
<json::object
> (tool_obj
,
1831 property_spec_ref ("tool", "driver",
1836 if (auto rule_obj
= lookup_rule_by_id_in_component (rule_id
, *driver_obj
))
1838 tool_component_obj
= driver_obj
;
1842 // TODO: also handle extensions
1847 const json::object
*
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");
1855 = get_optional_property
<json::array
> (tool_component_obj
, rules
);
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");
1867 = get_required_property
<json::string
> (*reporting_desc_obj
, id
);
1871 if (!strcmp (rule_id
, desc_id_jstr
->get_string ()))
1872 return reporting_desc_obj
;
1879 } // anonymous namespace
1881 /* Error-checking at the API boundary. */
1883 #define FAIL_IF_NULL(PTR_ARG) \
1885 GCC_DIAGNOSTIC_PUSH_IGNORED(-Wnonnull-compare) \
1887 fprintf (stderr, "%s: %s must be non-NULL\n", \
1888 __func__, #PTR_ARG); \
1891 GCC_DIAGNOSTIC_POP \
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
);