1 /* SARIF output for diagnostics
2 Copyright (C) 2018-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 3, or (at your option) any later
12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
25 #define INCLUDE_MEMORY
26 #define INCLUDE_VECTOR
28 #include "coretypes.h"
29 #include "diagnostic.h"
30 #include "diagnostic-metadata.h"
31 #include "diagnostic-path.h"
32 #include "diagnostic-format.h"
35 #include "logical-location.h"
36 #include "diagnostic-client-data-hooks.h"
37 #include "diagnostic-diagram.h"
38 #include "text-art/canvas.h"
39 #include "diagnostic-format-sarif.h"
40 #include "ordered-hash-map.h"
42 #include "make-unique.h"
44 #include "selftest-diagnostic.h"
45 #include "selftest-diagnostic-show-locus.h"
46 #include "selftest-json.h"
47 #include "text-range-label.h"
48 #include "pretty-print-format-impl.h"
49 #include "pretty-print-urlifier.h"
53 class content_renderer
;
54 class escape_nonascii_renderer
;
56 /* Subclasses of sarif_object.
57 Keep these in order of their descriptions in the specification. */
58 class sarif_artifact_content
; // 3.3
59 class sarif_artifact_location
; // 3.4
60 class sarif_message
; // 3.11
61 class sarif_multiformat_message_string
; // 3.12
62 class sarif_log
; // 3.13
63 class sarif_run
; // 3.14
64 class sarif_tool
; // 3.18
65 class sarif_tool_component
; // 3.19
66 class sarif_invocation
; // 3.20
67 class sarif_artifact
; // 3.24
68 class sarif_location_manager
; // not in the spec
69 class sarif_result
; // 3.27
70 class sarif_location
; // 3.28
71 class sarif_physical_location
; // 3.29
72 class sarif_region
; // 3.30
73 class sarif_logical_location
; // 3.33
74 class sarif_location_relationship
; // 3.34
75 class sarif_code_flow
; // 3.36
76 class sarif_thread_flow
; // 3.37
77 class sarif_thread_flow_location
; // 3.38
78 class sarif_reporting_descriptor
; // 3.49
79 class sarif_reporting_descriptor_reference
; // 3.53
80 class sarif_tool_component_reference
; // 3.54
81 class sarif_fix
; // 3.55
82 class sarif_artifact_change
; // 3.56
83 class sarif_replacement
; // 3.57
84 class sarif_ice_notification
; // 3.58
86 // Valid values for locationRelationship's "kinds" property (3.34.3)
88 enum class location_relationship_kind
97 /* Declarations of subclasses of sarif_object.
98 Keep these in order of their descriptions in the specification. */
100 /* Subclass of sarif_object for SARIF "artifactContent" objects
101 (SARIF v2.1.0 section 3.3). */
103 class sarif_artifact_content
: public sarif_object
{};
105 /* Subclass of sarif_object for SARIF "artifactLocation" objects
106 (SARIF v2.1.0 section 3.4). */
108 class sarif_artifact_location
: public sarif_object
{};
110 /* Subclass of sarif_object for SARIF "message" objects
111 (SARIF v2.1.0 section 3.11). */
113 class sarif_message
: public sarif_object
{};
115 /* Subclass of sarif_object for SARIF "multiformatMessageString" objects
116 (SARIF v2.1.0 section 3.12). */
118 class sarif_multiformat_message_string
: public sarif_object
{};
120 /* Subclass of sarif_object for SARIF "log" objects
121 (SARIF v2.1.0 section 3.13). */
123 class sarif_log
: public sarif_object
{};
125 /* Subclass of sarif_object for SARIF "run" objects
126 (SARIF v2.1.0 section 3.14). */
128 class sarif_run
: public sarif_object
{};
130 /* Subclass of sarif_object for SARIF "tool" objects
131 (SARIF v2.1.0 section 3.18). */
133 class sarif_tool
: public sarif_object
{};
135 /* Subclass of sarif_object for SARIF "toolComponent" objects
136 (SARIF v2.1.0 section 3.19). */
138 class sarif_tool_component
: public sarif_object
{};
140 /* Make a JSON string for the current date and time.
141 See SARIF v2.1.0 section 3.9 "Date/time properties".
142 Given that we don't run at the very beginning/end of the
143 process, it doesn't make sense to be more accurate than
144 the current second. */
146 static std::unique_ptr
<json::string
>
147 make_date_time_string_for_current_time ()
149 time_t t
= time (nullptr);
150 struct tm
*tm
= gmtime (&t
);
152 snprintf (buf
, sizeof (buf
) - 1,
155 tm
->tm_year
+ 1900, tm
->tm_mon
+ 1, tm
->tm_mday
,
156 tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
157 return ::make_unique
<json::string
> (buf
);
160 /* Subclass of sarif_object for SARIF "invocation" objects
161 (SARIF v2.1.0 section 3.20). */
163 class sarif_invocation
: public sarif_object
166 sarif_invocation (sarif_builder
&builder
,
167 const char * const *original_argv
);
169 void add_notification_for_ice (const diagnostic_info
&diagnostic
,
170 sarif_builder
&builder
);
171 void prepare_to_flush (sarif_builder
&builder
);
174 std::unique_ptr
<json::array
> m_notifications_arr
;
178 /* Corresponds to values for the SARIF artifact objects "roles" property.
179 (SARIF v2.1.0 section 3.24.6). */
181 enum class diagnostic_artifact_role
183 analysis_target
, /* "analysisTarget". */
184 debug_output_file
, /* "debugOutputFile". */
185 result_file
, /* "resultFile". */
187 /* "scannedFile" added in 2.2;
188 see https://github.com/oasis-tcs/sarif-spec/issues/459 */
191 traced_file
, /* "tracedFile". */
196 /* Subclass of sarif_object for SARIF artifact objects
197 (SARIF v2.1.0 section 3.24). */
199 class sarif_artifact
: public sarif_object
202 sarif_artifact (const char *filename
)
203 : m_filename (filename
),
204 m_roles ((unsigned)diagnostic_artifact_role::NUM_ROLES
),
205 m_embed_contents (false)
207 bitmap_clear (m_roles
);
210 void add_role (enum diagnostic_artifact_role role
,
211 bool embed_contents
);
213 bool embed_contents_p () const { return m_embed_contents
; }
214 void populate_contents (sarif_builder
&builder
);
215 void populate_roles ();
218 const char *m_filename
;
219 auto_sbitmap m_roles
;
221 /* Flag to track whether this artifact should have a "contents" property
222 (SARIF v2.1.0 section 3.24.8).
223 We only add the contents for those artifacts that have a location
224 referencing them (so that a consumer might want to quote the source). */
225 bool m_embed_contents
;
228 /* A class for sarif_objects that own a "namespace" of numeric IDs for
229 managing location objects within them. Currently (SARIF v2.1.0)
230 this is just for sarif_result (section 3.28.2), but it will likely
231 eventually also be for notification objects; see
232 https://github.com/oasis-tcs/sarif-spec/issues/540
234 Consider locations with chains of include information e.g.
237 > #include "include-chain-1.h"
240 | // First set of decls, which will be referenced in notes
241 | #include "include-chain-1-1.h"
243 | // Second set of decls, which will trigger the errors
244 | #include "include-chain-1-2.h"
254 GCC's textual output emits:
255 | In file included from PATH/include-chain-1.h:5,
256 | from PATH/include-chain-1.c:30:
257 | PATH/include-chain-1-2.h:1:6: error: conflicting types for 'p'; have 'char'
260 | In file included from PATH/include-chain-1.h:2:
261 | PATH/include-chain-1-1.h:1:5: note: previous declaration of 'p' with type 'int'
264 | PATH/include-chain-1-2.h:2:6: error: conflicting types for 'q'; have 'char'
267 | PATH/include-chain-1-1.h:2:5: note: previous declaration of 'q' with type 'int'
271 Whenever a SARIF location is added for a location_t that
272 was #included from somewhere, we queue up the creation of a SARIF
273 location for the location of the #include. The worklist of queued
274 locations is flushed when the result is finished, which lazily creates
275 any additional related locations for the include chain, and the
276 relationships between the locations. Doing so can lead to further
277 include locations being processed. The worklist approach allows us
278 to lazily explore the relevant part of the directed graph of location_t
279 values implicit in our line_maps structure, replicating it as a directed
280 graph of SARIF locations within the SARIF result object, like this:
282 [0]: error in include-chain-1-2.h ("conflicting types for 'p'; have 'char'")
283 [1]: #include "include-chain-1-2.h" in include-chain-1.h
284 [2]: note in include-chain-1-2.h ("previous declaration of 'p' with type 'int'")
285 [3]: #include "include-chain-1-1.h" in include-chain-1.h
286 [4]: #include "include-chain-1.h" in include-chain-1.c
288 where we want to capture this "includes" graph in SARIF form:
289 . +-----------------------------------+ +----------------------------------+
290 . |"id": 0 | |"id": 2 |
291 . | error: "conflicting types for 'p';| | note: previous declaration of 'p'|
292 . | have 'char'"| | | with type 'int'") |
293 . | in include-chain-1-2.h | | in include-chain-1-1.h |
294 . +-----------------------------------+ +----------------------------------+
296 . includes | | included-by includes | | included-by
298 . +--------------------------------+ +--------------------------------+
299 . |"id": 1 | |"id": 3 |
300 . | #include "include-chain-1-2.h" | | #include "include-chain-1-1.h" |
301 . | in include-chain-1.h | | in include-chain-1.h |
302 . +--------------------------------+ +--------------------------------+
304 . includes | | included-by includes | | included-by
306 . +------------------------------------+
308 . | The #include "include-chain-1.h" |
309 . | in include-chain-1.c |
310 . +------------------------------------+
313 class sarif_location_manager
: public sarif_object
316 /* A worklist of pending actions needed to fully process this object.
318 This lets us lazily walk our data structures to build the
319 directed graph of locations, whilst keeping "notes" at the top
320 of the "relatedLocations" array, and avoiding the need for
326 /* Process a #include relationship where m_location_obj
327 was #included-d at m_where. */
330 /* Process a location_t that was added as a secondary location
331 to a rich_location without a label. */
332 unlabelled_secondary_location
335 worklist_item (sarif_location
&location_obj
,
338 : m_location_obj (location_obj
),
344 sarif_location
&m_location_obj
;
349 sarif_location_manager ()
350 : m_related_locations_arr (nullptr),
351 m_next_location_id (0)
355 unsigned allocate_location_id ()
357 return m_next_location_id
++;
361 add_related_location (std::unique_ptr
<sarif_location
> location_obj
,
362 sarif_builder
&builder
);
365 add_relationship_to_worklist (sarif_location
&location_obj
,
366 enum worklist_item::kind kind
,
370 process_worklist (sarif_builder
&builder
);
373 process_worklist_item (sarif_builder
&builder
,
374 const worklist_item
&item
);
376 json::array
*m_related_locations_arr
; // borrowed
377 unsigned m_next_location_id
;
379 std::list
<worklist_item
> m_worklist
;
380 std::map
<location_t
, sarif_location
*> m_included_from_locations
;
381 std::map
<location_t
, sarif_location
*> m_unlabelled_secondary_locations
;
384 /* Subclass of sarif_object for SARIF "result" objects
385 (SARIF v2.1.0 section 3.27).
386 Each SARIF result object has its own "namespace" of numeric IDs for
387 managing location objects (SARIF v2.1.0 section 3.28.2). */
389 class sarif_result
: public sarif_location_manager
392 sarif_result (unsigned idx_within_parent
)
393 : m_idx_within_parent (idx_within_parent
)
396 unsigned get_index_within_parent () const { return m_idx_within_parent
; }
399 on_nested_diagnostic (const diagnostic_info
&diagnostic
,
400 diagnostic_t orig_diag_kind
,
401 sarif_builder
&builder
);
402 void on_diagram (const diagnostic_diagram
&diagram
,
403 sarif_builder
&builder
);
406 const unsigned m_idx_within_parent
;
409 /* Subclass of sarif_object for SARIF "location" objects
410 (SARIF v2.1.0 section 3.28).
411 A location object can have an "id" which must be unique within
412 the enclosing result, if any (see SARIF v2.1.0 section 3.28.2). */
414 class sarif_location
: public sarif_object
417 long lazily_add_id (sarif_location_manager
&loc_mgr
);
418 long get_id () const;
420 void lazily_add_relationship (sarif_location
&target
,
421 enum location_relationship_kind kind
,
422 sarif_location_manager
&loc_mgr
);
425 sarif_location_relationship
&
426 lazily_add_relationship_object (sarif_location
&target
,
427 sarif_location_manager
&loc_mgr
);
429 json::array
&lazily_add_relationships_array ();
431 std::map
<sarif_location
*,
432 sarif_location_relationship
*> m_relationships_map
;
435 /* Subclass of sarif_object for SARIF "physicalLocation" objects
436 (SARIF v2.1.0 section 3.29). */
438 class sarif_physical_location
: public sarif_object
{};
440 /* Subclass of sarif_object for SARIF "region" objects
441 (SARIF v2.1.0 section 3.30). */
443 class sarif_region
: public sarif_object
{};
445 /* Subclass of sarif_object for SARIF "locationRelationship" objects
446 (SARIF v2.1.0 section 3.34). */
448 class sarif_location_relationship
: public sarif_object
451 sarif_location_relationship (sarif_location
&target
,
452 sarif_location_manager
&loc_mgr
);
454 long get_target_id () const;
456 void lazily_add_kind (enum location_relationship_kind kind
);
459 auto_sbitmap m_kinds
;
462 /* Subclass of sarif_object for SARIF "codeFlow" objects
463 (SARIF v2.1.0 section 3.36). */
465 class sarif_code_flow
: public sarif_object
468 sarif_code_flow (sarif_result
&parent
,
469 unsigned idx_within_parent
);
471 sarif_result
&get_parent () const { return m_parent
; }
472 unsigned get_index_within_parent () const { return m_idx_within_parent
; }
475 get_or_append_thread_flow (const diagnostic_thread
&thread
,
476 diagnostic_thread_id_t thread_id
);
479 get_thread_flow (diagnostic_thread_id_t thread_id
);
481 void add_location (sarif_thread_flow_location
&);
483 sarif_thread_flow_location
&
484 get_thread_flow_loc_obj (diagnostic_event_id_t event_id
) const;
487 sarif_result
&m_parent
;
488 const unsigned m_idx_within_parent
;
490 hash_map
<int_hash
<diagnostic_thread_id_t
, -1, -2>,
491 sarif_thread_flow
*> m_thread_id_map
; // borrowed ptr
492 json::array
*m_thread_flows_arr
; // borrowed
494 /* Vec of borrowed ptr, allowing for going easily from
495 an event_id to the corresponding threadFlowLocation object. */
496 std::vector
<sarif_thread_flow_location
*> m_all_tfl_objs
;
499 /* Subclass of sarif_object for SARIF "threadFlow" objects
500 (SARIF v2.1.0 section 3.37). */
502 class sarif_thread_flow
: public sarif_object
505 sarif_thread_flow (sarif_code_flow
&parent
,
506 const diagnostic_thread
&thread
,
507 unsigned idx_within_parent
);
509 sarif_code_flow
&get_parent () const { return m_parent
; }
510 unsigned get_index_within_parent () const { return m_idx_within_parent
; }
512 sarif_thread_flow_location
&add_location ();
515 sarif_code_flow
&m_parent
;
516 json::array
*m_locations_arr
; // borrowed
517 const unsigned m_idx_within_parent
;
520 /* Subclass of sarif_object for SARIF "threadFlowLocation" objects
521 (SARIF v2.1.0 section 3.38). */
523 class sarif_thread_flow_location
: public sarif_object
526 sarif_thread_flow_location (sarif_thread_flow
&parent
,
527 unsigned idx_within_parent
)
529 m_idx_within_parent (idx_within_parent
)
533 sarif_thread_flow
&get_parent () const { return m_parent
; }
534 unsigned get_index_within_parent () const { return m_idx_within_parent
; }
537 sarif_thread_flow
&m_parent
;
538 const unsigned m_idx_within_parent
;
541 /* Subclass of sarif_object for SARIF "reportingDescriptor" objects
542 (SARIF v2.1.0 section 3.49). */
544 class sarif_reporting_descriptor
: public sarif_object
{};
546 /* Subclass of sarif_object for SARIF "reportingDescriptorReference" objects
547 (SARIF v2.1.0 section 3.53). */
549 class sarif_reporting_descriptor_reference
: public sarif_object
{};
551 /* Subclass of sarif_object for SARIF "toolComponentReference" objects
552 (SARIF v2.1.0 section 3.54). */
554 class sarif_tool_component_reference
: public sarif_object
{};
556 /* Subclass of sarif_object for SARIF "fix" objects
557 (SARIF v2.1.0 section 3.55). */
559 class sarif_fix
: public sarif_object
{};
561 /* Subclass of sarif_object for SARIF "artifactChange" objects
562 (SARIF v2.1.0 section 3.56). */
564 class sarif_artifact_change
: public sarif_object
{};
566 /* Subclass of sarif_object for SARIF "replacement" objects
567 (SARIF v2.1.0 section 3.57). */
569 class sarif_replacement
: public sarif_object
{};
571 /* Subclass of sarif_object for SARIF "notification" objects
572 (SARIF v2.1.0 section 3.58).
574 This subclass is specifically for notifying when an
575 internal compiler error occurs. */
577 class sarif_ice_notification
: public sarif_location_manager
580 sarif_ice_notification (const diagnostic_info
&diagnostic
,
581 sarif_builder
&builder
);
584 add_related_location (std::unique_ptr
<sarif_location
> location_obj
,
585 sarif_builder
&builder
) final override
;
588 /* Abstract base class for use when making an "artifactContent"
589 object (SARIF v2.1.0 section 3.3): generate a value for the
590 3.3.4 "rendered" property.
591 Can return nullptr, for "no property". */
593 class content_renderer
596 virtual ~content_renderer () {}
598 virtual std::unique_ptr
<sarif_multiformat_message_string
>
599 render (const sarif_builder
&builder
) const = 0;
602 /* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
603 and -fdiagnostics-format=sarif-file).
605 As diagnostics occur, we build "result" JSON objects, and
607 - which source files are referenced
608 - which warnings are emitted
609 - which CWEs are used
611 At the end of the compile, we use the above to build the full SARIF
612 object tree, adding the result objects to the correct place, and
613 creating objects for the various source files, warnings and CWEs
619 - diagnostic groups (see limitations below)
620 - logical locations (e.g. cfun)
621 - labelled ranges (as annotations)
622 - secondary ranges without labels (as related locations)
625 - GCC supports one-deep nesting of diagnostics (via auto_diagnostic_group),
626 but we only capture location and message information from such nested
627 diagnostics (e.g. we ignore fix-it hints on them)
628 - although we capture command-line arguments (section 3.20.2), we don't
629 yet capture response files.
630 - doesn't capture "artifact.encoding" property
631 (SARIF v2.1.0 section 3.24.9).
632 - doesn't capture hashes of the source files
633 ("artifact.hashes" property (SARIF v2.1.0 section 3.24.11).
634 - doesn't capture the "analysisTarget" property
635 (SARIF v2.1.0 section 3.27.13).
636 - doesn't capture -Werror cleanly
637 - doesn't capture inlining information (can SARIF handle this?)
638 - doesn't capture macro expansion information (can SARIF handle this?).
639 - doesn't capture any diagnostic_metadata::rules associated with
645 sarif_builder (diagnostic_context
&context
,
646 const line_maps
*line_maps
,
647 const char *main_input_filename_
,
649 enum sarif_version version
);
652 void on_report_diagnostic (const diagnostic_info
&diagnostic
,
653 diagnostic_t orig_diag_kind
);
654 void emit_diagram (const diagnostic_diagram
&diagram
);
657 std::unique_ptr
<sarif_result
> take_current_result ()
659 return std::move (m_cur_group_result
);
662 std::unique_ptr
<sarif_log
> flush_to_object ();
663 void flush_to_file (FILE *outf
);
665 std::unique_ptr
<json::array
>
666 make_locations_arr (sarif_location_manager
&loc_mgr
,
667 const diagnostic_info
&diagnostic
,
668 enum diagnostic_artifact_role role
);
669 std::unique_ptr
<sarif_location
>
670 make_location_object (sarif_location_manager
&loc_mgr
,
671 const rich_location
&rich_loc
,
672 const logical_location
*logical_loc
,
673 enum diagnostic_artifact_role role
);
674 std::unique_ptr
<sarif_location
>
675 make_location_object (sarif_location_manager
&loc_mgr
,
677 enum diagnostic_artifact_role role
);
678 std::unique_ptr
<sarif_message
>
679 make_message_object (const char *msg
) const;
680 std::unique_ptr
<sarif_message
>
681 make_message_object_for_diagram (const diagnostic_diagram
&diagram
);
682 std::unique_ptr
<sarif_artifact_content
>
683 maybe_make_artifact_content_object (const char *filename
) const;
685 std::unique_ptr
<sarif_artifact_location
>
686 make_artifact_location_object (const char *filename
);
688 const sarif_code_flow
*
689 get_code_flow_for_event_ids () const
691 return m_current_code_flow
;
694 diagnostic_context
&get_context () const { return m_context
; }
695 pretty_printer
*get_printer () const { return m_printer
; }
696 token_printer
&get_token_printer () { return m_token_printer
; }
697 enum sarif_version
get_version () const { return m_version
; }
700 class sarif_token_printer
: public token_printer
703 sarif_token_printer (sarif_builder
&builder
)
704 : m_builder (builder
)
707 void print_tokens (pretty_printer
*pp
,
708 const pp_token_list
&tokens
) final override
;
710 sarif_builder
&m_builder
;
713 std::unique_ptr
<sarif_result
>
714 make_result_object (const diagnostic_info
&diagnostic
,
715 diagnostic_t orig_diag_kind
,
716 unsigned idx_within_parent
);
718 add_any_include_chain (sarif_location_manager
&loc_mgr
,
719 sarif_location
&location_obj
,
722 set_any_logical_locs_arr (sarif_location
&location_obj
,
723 const logical_location
*logical_loc
);
724 std::unique_ptr
<sarif_location
>
725 make_location_object (sarif_location_manager
&loc_mgr
,
726 const diagnostic_event
&event
,
727 enum diagnostic_artifact_role role
);
728 std::unique_ptr
<sarif_code_flow
>
729 make_code_flow_object (sarif_result
&result
,
730 unsigned idx_within_parent
,
731 const diagnostic_path
&path
);
733 populate_thread_flow_location_object (sarif_result
&result
,
734 sarif_thread_flow_location
&thread_flow_loc_obj
,
735 const diagnostic_event
&event
,
736 int event_execution_idx
);
737 std::unique_ptr
<json::array
>
738 maybe_make_kinds_array (diagnostic_event::meaning m
) const;
739 std::unique_ptr
<sarif_physical_location
>
740 maybe_make_physical_location_object (location_t loc
,
741 enum diagnostic_artifact_role role
,
743 const content_renderer
*snippet_renderer
);
744 std::unique_ptr
<sarif_artifact_location
>
745 make_artifact_location_object (location_t loc
);
746 std::unique_ptr
<sarif_artifact_location
>
747 make_artifact_location_object_for_pwd () const;
748 std::unique_ptr
<sarif_region
>
749 maybe_make_region_object (location_t loc
,
750 int column_override
) const;
751 std::unique_ptr
<sarif_region
>
752 maybe_make_region_object_for_context (location_t loc
,
753 const content_renderer
*snippet_renderer
) const;
754 std::unique_ptr
<sarif_region
>
755 make_region_object_for_hint (const fixit_hint
&hint
) const;
756 std::unique_ptr
<sarif_multiformat_message_string
>
757 make_multiformat_message_string (const char *msg
) const;
758 std::unique_ptr
<sarif_log
>
759 make_top_level_object (std::unique_ptr
<sarif_invocation
> invocation_obj
,
760 std::unique_ptr
<json::array
> results
);
761 std::unique_ptr
<sarif_run
>
762 make_run_object (std::unique_ptr
<sarif_invocation
> invocation_obj
,
763 std::unique_ptr
<json::array
> results
);
764 std::unique_ptr
<sarif_tool
>
766 std::unique_ptr
<sarif_tool_component
>
767 make_driver_tool_component_object ();
768 std::unique_ptr
<json::array
> maybe_make_taxonomies_array () const;
769 std::unique_ptr
<sarif_tool_component
>
770 maybe_make_cwe_taxonomy_object () const;
771 std::unique_ptr
<sarif_tool_component_reference
>
772 make_tool_component_reference_object_for_cwe () const;
773 std::unique_ptr
<sarif_reporting_descriptor
>
774 make_reporting_descriptor_object_for_warning (const diagnostic_info
&diagnostic
,
775 diagnostic_t orig_diag_kind
,
776 const char *option_text
);
777 std::unique_ptr
<sarif_reporting_descriptor
>
778 make_reporting_descriptor_object_for_cwe_id (int cwe_id
) const;
779 std::unique_ptr
<sarif_reporting_descriptor_reference
>
780 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id
);
782 get_or_create_artifact (const char *filename
,
783 enum diagnostic_artifact_role role
,
784 bool embed_contents
);
786 get_source_lines (const char *filename
,
789 std::unique_ptr
<sarif_artifact_content
>
790 maybe_make_artifact_content_object (const char *filename
,
793 const content_renderer
*r
) const;
794 std::unique_ptr
<sarif_fix
>
795 make_fix_object (const rich_location
&rich_loc
);
796 std::unique_ptr
<sarif_artifact_change
>
797 make_artifact_change_object (const rich_location
&richloc
);
798 std::unique_ptr
<sarif_replacement
>
799 make_replacement_object (const fixit_hint
&hint
) const;
800 std::unique_ptr
<sarif_artifact_content
>
801 make_artifact_content_object (const char *text
) const;
802 int get_sarif_column (expanded_location exploc
) const;
804 diagnostic_context
&m_context
;
805 pretty_printer
*m_printer
;
806 const line_maps
*m_line_maps
;
807 sarif_token_printer m_token_printer
;
808 enum sarif_version m_version
;
810 /* The JSON object for the invocation object. */
811 std::unique_ptr
<sarif_invocation
> m_invocation_obj
;
813 /* The JSON array of pending diagnostics. */
814 std::unique_ptr
<json::array
> m_results_array
;
816 /* The JSON object for the result object (if any) in the current
818 std::unique_ptr
<sarif_result
> m_cur_group_result
;
820 /* Ideally we'd use std::unique_ptr<sarif_artifact> here, but I had
821 trouble getting this to work when building with GCC 4.8. */
822 ordered_hash_map
<nofree_string_hash
,
823 sarif_artifact
*> m_filename_to_artifact_map
;
825 bool m_seen_any_relative_paths
;
826 hash_set
<free_string_hash
> m_rule_id_set
;
827 std::unique_ptr
<json::array
> m_rules_arr
;
829 /* The set of all CWE IDs we've seen, if any. */
830 hash_set
<int_hash
<int, 0, 1> > m_cwe_id_set
;
836 unsigned m_next_result_idx
;
837 sarif_code_flow
*m_current_code_flow
;
840 /* class sarif_object : public json::object. */
843 sarif_object::get_or_create_properties ()
845 json::value
*properties_val
= get ("properties");
848 if (properties_val
->get_kind () == json::JSON_OBJECT
)
849 return *static_cast <sarif_property_bag
*> (properties_val
);
852 sarif_property_bag
*bag
= new sarif_property_bag ();
853 set ("properties", bag
);
857 /* class sarif_invocation : public sarif_object. */
859 sarif_invocation::sarif_invocation (sarif_builder
&builder
,
860 const char * const *original_argv
)
861 : m_notifications_arr (::make_unique
<json::array
> ()),
864 // "arguments" property (SARIF v2.1.0 section 3.20.2)
867 auto arguments_arr
= ::make_unique
<json::array
> ();
868 for (size_t i
= 0; original_argv
[i
]; ++i
)
869 arguments_arr
->append_string (original_argv
[i
]);
870 set
<json::array
> ("arguments", std::move (arguments_arr
));
873 // "workingDirectory" property (SARIF v2.1.0 section 3.20.19)
874 if (const char *pwd
= getpwd ())
875 set
<sarif_artifact_location
> ("workingDirectory",
876 builder
.make_artifact_location_object (pwd
));
878 // "startTimeUtc" property (SARIF v2.1.0 section 3.20.7)
879 set
<json::string
> ("startTimeUtc",
880 make_date_time_string_for_current_time ());
883 /* Handle an internal compiler error DIAGNOSTIC.
884 Add an object representing the ICE to the notifications array. */
887 sarif_invocation::add_notification_for_ice (const diagnostic_info
&diagnostic
,
888 sarif_builder
&builder
)
893 = ::make_unique
<sarif_ice_notification
> (diagnostic
, builder
);
895 /* Support for related locations within a notification was added
896 in SARIF 2.2; see https://github.com/oasis-tcs/sarif-spec/issues/540 */
897 if (builder
.get_version () >= sarif_version::v2_2_prerelease_2024_08_08
)
898 notification
->process_worklist (builder
);
900 m_notifications_arr
->append
<sarif_ice_notification
>
901 (std::move (notification
));
905 sarif_invocation::prepare_to_flush (sarif_builder
&builder
)
907 const diagnostic_context
&context
= builder
.get_context ();
909 /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
910 if (context
.execution_failed_p ())
912 set_bool ("executionSuccessful", m_success
);
914 /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
915 set ("toolExecutionNotifications", std::move (m_notifications_arr
));
917 /* Call client hook, allowing it to create a custom property bag for
918 this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */
919 if (auto client_data_hooks
= context
.get_client_data_hooks ())
920 client_data_hooks
->add_sarif_invocation_properties (*this);
922 // "endTimeUtc" property (SARIF v2.1.0 section 3.20.8);
923 set
<json::string
> ("endTimeUtc",
924 make_date_time_string_for_current_time ());
927 /* class sarif_artifact : public sarif_object. */
929 /* Add ROLE to this artifact's roles.
930 If EMBED_CONTENTS is true, then flag that we will attempt to embed the
931 contents of this artifact when writing it out. */
934 sarif_artifact::add_role (enum diagnostic_artifact_role role
,
937 /* TODO(SARIF 2.2): "scannedFile" is to be added as a role in SARIF 2.2;
938 see https://github.com/oasis-tcs/sarif-spec/issues/459
941 Ultimately, we probably shouldn't bother embedding the contents
942 of such artifacts, just the snippets. */
943 if (role
== diagnostic_artifact_role::scanned_file
)
947 m_embed_contents
= true;
949 /* In SARIF v2.1.0 section 3.24.6 "roles" property:
950 "resultFile" is for an artifact
951 "which the analysis tool was not explicitly instructed to scan",
952 whereas "analysisTarget" is for one where the
953 "analysis tool was instructed to scan this artifact".
954 Hence the latter excludes the former. */
955 if (role
== diagnostic_artifact_role::result_file
)
956 if (bitmap_bit_p (m_roles
, (int)diagnostic_artifact_role::analysis_target
))
959 bitmap_set_bit (m_roles
, (int)role
);
962 /* Populate the "contents" property (SARIF v2.1.0 section 3.24.8).
963 We do this after initialization to
964 (a) ensure that any charset options have been set
965 (b) only populate it for artifacts that are referenced by a location. */
968 sarif_artifact::populate_contents (sarif_builder
&builder
)
970 if (auto artifact_content_obj
971 = builder
.maybe_make_artifact_content_object (m_filename
))
972 set
<sarif_artifact_content
> ("contents", std::move (artifact_content_obj
));
975 /* Get a string for ROLE corresponding to the
976 SARIF v2.1.0 section 3.24.6 "roles" property. */
979 get_artifact_role_string (enum diagnostic_artifact_role role
)
985 case diagnostic_artifact_role::analysis_target
:
986 return "analysisTarget";
987 case diagnostic_artifact_role::debug_output_file
:
988 return "debugOutputFile";
989 case diagnostic_artifact_role::result_file
:
991 case diagnostic_artifact_role::scanned_file
:
992 return "scannedFile";
993 case diagnostic_artifact_role::traced_file
:
998 /* Populate the "roles" property of this sarif_artifact with a new
999 json::array for the artifact.roles property (SARIF v2.1.0 section 3.24.6)
1000 containing strings such as "analysisTarget", "resultFile"
1001 and/or "tracedFile". */
1004 sarif_artifact::populate_roles ()
1006 if (bitmap_empty_p (m_roles
))
1008 auto roles_arr (::make_unique
<json::array
> ());
1009 for (int i
= 0; i
< (int)diagnostic_artifact_role::NUM_ROLES
; i
++)
1010 if (bitmap_bit_p (m_roles
, i
))
1012 enum diagnostic_artifact_role role
= (enum diagnostic_artifact_role
)i
;
1013 roles_arr
->append_string (get_artifact_role_string (role
));
1015 set
<json::array
> ("roles", std::move (roles_arr
));
1018 /* class sarif_location_manager : public sarif_object. */
1020 /* Base implementation of sarif_location_manager::add_related_location vfunc.
1022 Add LOCATION_OBJ to this object's "relatedLocations" array,
1023 creating it if it doesn't yet exist. */
1026 sarif_location_manager::
1027 add_related_location (std::unique_ptr
<sarif_location
> location_obj
,
1030 if (!m_related_locations_arr
)
1032 m_related_locations_arr
= new json::array ();
1033 /* Give ownership of m_related_locations_arr to json::object;
1034 keep a borrowed ptr. */
1035 set ("relatedLocations", m_related_locations_arr
);
1037 m_related_locations_arr
->append (std::move (location_obj
));
1041 sarif_location_manager::
1042 add_relationship_to_worklist (sarif_location
&location_obj
,
1043 enum worklist_item::kind kind
,
1046 m_worklist
.push_back (worklist_item (location_obj
,
1051 /* Process all items in this result's worklist.
1052 Doing so may temporarily add new items to the end
1054 Handling any item should be "lazy", and thus we should
1055 eventually drain the queue and terminate. */
1058 sarif_location_manager::process_worklist (sarif_builder
&builder
)
1060 while (!m_worklist
.empty ())
1062 const worklist_item
&item
= m_worklist
.front ();
1063 process_worklist_item (builder
, item
);
1064 m_worklist
.pop_front ();
1068 /* Process one item in this result's worklist, potentially
1069 adding new items to the end of the worklist. */
1072 sarif_location_manager::process_worklist_item (sarif_builder
&builder
,
1073 const worklist_item
&item
)
1075 switch (item
.m_kind
)
1079 case worklist_item::kind::included_from
:
1081 sarif_location
&included_loc_obj
= item
.m_location_obj
;
1082 sarif_location
*includer_loc_obj
= nullptr;
1083 auto iter
= m_included_from_locations
.find (item
.m_where
);
1084 if (iter
!= m_included_from_locations
.end ())
1085 includer_loc_obj
= iter
->second
;
1088 std::unique_ptr
<sarif_location
> new_loc_obj
1089 = builder
.make_location_object
1092 diagnostic_artifact_role::scanned_file
);
1093 includer_loc_obj
= new_loc_obj
.get ();
1094 add_related_location (std::move (new_loc_obj
), builder
);
1096 = std::pair
<location_t
, sarif_location
*> (item
.m_where
,
1098 m_included_from_locations
.insert (kv
);
1101 includer_loc_obj
->lazily_add_relationship
1103 location_relationship_kind::includes
,
1105 included_loc_obj
.lazily_add_relationship
1107 location_relationship_kind::is_included_by
,
1111 case worklist_item::kind::unlabelled_secondary_location
:
1113 sarif_location
&primary_loc_obj
= item
.m_location_obj
;
1114 sarif_location
*secondary_loc_obj
= nullptr;
1115 auto iter
= m_unlabelled_secondary_locations
.find (item
.m_where
);
1116 if (iter
!= m_unlabelled_secondary_locations
.end ())
1117 secondary_loc_obj
= iter
->second
;
1120 std::unique_ptr
<sarif_location
> new_loc_obj
1121 = builder
.make_location_object
1124 diagnostic_artifact_role::scanned_file
);
1125 secondary_loc_obj
= new_loc_obj
.get ();
1126 add_related_location (std::move (new_loc_obj
), builder
);
1128 = std::pair
<location_t
, sarif_location
*> (item
.m_where
,
1130 m_unlabelled_secondary_locations
.insert (kv
);
1132 gcc_assert (secondary_loc_obj
);
1133 primary_loc_obj
.lazily_add_relationship
1134 (*secondary_loc_obj
,
1135 location_relationship_kind::relevant
,
1142 /* class sarif_result : public sarif_location_manager. */
1144 /* Handle secondary diagnostics that occur within a diagnostic group.
1145 The closest SARIF seems to have to nested diagnostics is the
1146 "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
1147 so we lazily set this property and populate the array if and when
1148 secondary diagnostics occur (such as notes to a warning). */
1151 sarif_result::on_nested_diagnostic (const diagnostic_info
&diagnostic
,
1152 diagnostic_t
/*orig_diag_kind*/,
1153 sarif_builder
&builder
)
1155 /* We don't yet generate meaningful logical locations for notes;
1156 sometimes these will related to current_function_decl, but
1157 often they won't. */
1159 = builder
.make_location_object (*this, *diagnostic
.richloc
, nullptr,
1160 diagnostic_artifact_role::result_file
);
1162 = builder
.make_message_object (pp_formatted_text (builder
.get_printer ()));
1163 pp_clear_output_area (builder
.get_printer ());
1164 location_obj
->set
<sarif_message
> ("message", std::move (message_obj
));
1166 add_related_location (std::move (location_obj
), builder
);
1169 /* Handle diagrams that occur within a diagnostic group.
1170 The closest thing in SARIF seems to be to add a location to the
1171 "releatedLocations" property (SARIF v2.1.0 section 3.27.22),
1172 and to put the diagram into the "message" property of that location
1173 (SARIF v2.1.0 section 3.28.5). */
1176 sarif_result::on_diagram (const diagnostic_diagram
&diagram
,
1177 sarif_builder
&builder
)
1179 auto location_obj
= ::make_unique
<sarif_location
> ();
1180 auto message_obj
= builder
.make_message_object_for_diagram (diagram
);
1181 location_obj
->set
<sarif_message
> ("message", std::move (message_obj
));
1183 add_related_location (std::move (location_obj
), builder
);
1186 /* class sarif_location : public sarif_object. */
1188 /* Ensure this location has an "id" and return it.
1189 Use LOC_MGR if an id needs to be allocated.
1191 See the "id" property (3.28.2).
1193 We use this to only assign ids to locations that are
1194 referenced by another sarif object; others have no "id". */
1197 sarif_location::lazily_add_id (sarif_location_manager
&loc_mgr
)
1199 long id
= get_id ();
1202 id
= loc_mgr
.allocate_location_id ();
1203 set_integer ("id", id
);
1204 gcc_assert (id
!= -1);
1208 /* Get the id of this location, or -1 if it doesn't have one. */
1211 sarif_location::get_id () const
1213 json::value
*id
= get ("id");
1216 gcc_assert (id
->get_kind () == json::JSON_INTEGER
);
1217 return static_cast <json::integer_number
*> (id
)->get ();
1220 // 3.34.3 kinds property
1222 get_string_for_location_relationship_kind (enum location_relationship_kind kind
)
1228 case location_relationship_kind::includes
:
1230 case location_relationship_kind::is_included_by
:
1231 return "isIncludedBy";
1232 case location_relationship_kind::relevant
:
1237 /* Lazily populate this location's "relationships" property (3.28.7)
1238 with the relationship of KIND to TARGET, creating objects
1240 Use LOC_MGR for any locations that need "id" values. */
1243 sarif_location::lazily_add_relationship (sarif_location
&target
,
1244 enum location_relationship_kind kind
,
1245 sarif_location_manager
&loc_mgr
)
1247 sarif_location_relationship
&relationship_obj
1248 = lazily_add_relationship_object (target
, loc_mgr
);
1250 relationship_obj
.lazily_add_kind (kind
);
1253 /* Lazily populate this location's "relationships" property (3.28.7)
1254 with a location_relationship to TARGET, creating objects
1256 Use LOC_MGR for any locations that need "id" values. */
1258 sarif_location_relationship
&
1259 sarif_location::lazily_add_relationship_object (sarif_location
&target
,
1260 sarif_location_manager
&loc_mgr
)
1262 /* See if THIS already has a locationRelationship referencing TARGET. */
1263 auto iter
= m_relationships_map
.find (&target
);
1264 if (iter
!= m_relationships_map
.end ())
1266 /* We already have a locationRelationship from THIS to TARGET. */
1267 sarif_location_relationship
*relationship
= iter
->second
;
1268 gcc_assert (relationship
->get_target_id() == target
.get_id ());
1269 return *relationship
;
1272 // Ensure that THIS has a "relationships" property (3.28.7).
1273 json::array
&relationships_arr
= lazily_add_relationships_array ();
1275 /* No existing locationRelationship from THIS to TARGET; make one,
1276 record it, and add it to the "relationships" array. */
1277 auto relationship_obj
1278 = ::make_unique
<sarif_location_relationship
> (target
, loc_mgr
);
1279 sarif_location_relationship
*relationship
= relationship_obj
.get ();
1281 = std::pair
<sarif_location
*,
1282 sarif_location_relationship
*> (&target
, relationship
);
1283 m_relationships_map
.insert (kv
);
1285 relationships_arr
.append (std::move (relationship_obj
));
1287 return *relationship
;
1290 /* Ensure this location has a "relationships" array (3.28.7). */
1293 sarif_location::lazily_add_relationships_array ()
1295 const char *const property_name
= "relationships";
1296 if (json::value
*relationships
= get (property_name
))
1298 gcc_assert (relationships
->get_kind () == json::JSON_ARRAY
);
1299 return *static_cast <json::array
*> (relationships
);
1301 json::array
*relationships_arr
= new json::array ();
1302 set (property_name
, relationships_arr
);
1303 return *relationships_arr
;
1306 /* class sarif_ice_notification : public sarif_location_manager. */
1308 /* sarif_ice_notification's ctor.
1309 DIAGNOSTIC is an internal compiler error. */
1311 sarif_ice_notification::
1312 sarif_ice_notification (const diagnostic_info
&diagnostic
,
1313 sarif_builder
&builder
)
1315 /* "locations" property (SARIF v2.1.0 section 3.58.4). */
1317 = builder
.make_locations_arr (*this,
1319 diagnostic_artifact_role::result_file
);
1320 set
<json::array
> ("locations", std::move (locations_arr
));
1322 /* "message" property (SARIF v2.1.0 section 3.85.5). */
1324 = builder
.make_message_object (pp_formatted_text (builder
.get_printer ()));
1325 pp_clear_output_area (builder
.get_printer ());
1326 set
<sarif_message
> ("message", std::move (message_obj
));
1328 /* "level" property (SARIF v2.1.0 section 3.58.6). */
1329 set_string ("level", "error");
1332 /* Implementation of sarif_location_manager::add_related_location vfunc
1333 for notifications. */
1336 sarif_ice_notification::
1337 add_related_location (std::unique_ptr
<sarif_location
> location_obj
,
1338 sarif_builder
&builder
)
1340 /* Support for related locations within a notification was added
1341 in SARIF 2.2; see https://github.com/oasis-tcs/sarif-spec/issues/540 */
1342 if (builder
.get_version () >= sarif_version::v2_2_prerelease_2024_08_08
)
1343 sarif_location_manager::add_related_location (std::move (location_obj
),
1345 /* Otherwise implicitly discard LOCATION_OBJ. */
1348 /* class sarif_location_relationship : public sarif_object. */
1350 sarif_location_relationship::
1351 sarif_location_relationship (sarif_location
&target
,
1352 sarif_location_manager
&loc_mgr
)
1353 : m_kinds ((unsigned)location_relationship_kind::NUM_KINDS
)
1355 bitmap_clear (m_kinds
);
1356 set_integer ("target", target
.lazily_add_id (loc_mgr
));
1360 sarif_location_relationship::get_target_id () const
1362 json::value
*id
= get ("id");
1364 return static_cast <json::integer_number
*> (id
)->get ();
1368 sarif_location_relationship::
1369 lazily_add_kind (enum location_relationship_kind kind
)
1371 if (bitmap_bit_p (m_kinds
, (int)kind
))
1372 return; // already have this kind
1373 bitmap_set_bit (m_kinds
, (int)kind
);
1375 // 3.34.3 kinds property
1376 json::array
*kinds_arr
= nullptr;
1377 if (json::value
*kinds_val
= get ("kinds"))
1379 gcc_assert (kinds_val
->get_kind () == json::JSON_ARRAY
);
1383 kinds_arr
= new json::array ();
1384 set ("kinds", kinds_arr
);
1386 const char *kind_str
= get_string_for_location_relationship_kind (kind
);
1387 kinds_arr
->append_string (kind_str
);
1390 /* class sarif_code_flow : public sarif_object. */
1392 sarif_code_flow::sarif_code_flow (sarif_result
&parent
,
1393 unsigned idx_within_parent
)
1394 : m_parent (parent
),
1395 m_idx_within_parent (idx_within_parent
)
1397 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
1398 auto thread_flows_arr
= ::make_unique
<json::array
> ();
1399 m_thread_flows_arr
= thread_flows_arr
.get (); // borrowed
1400 set
<json::array
> ("threadFlows", std::move (thread_flows_arr
));
1404 sarif_code_flow::get_or_append_thread_flow (const diagnostic_thread
&thread
,
1405 diagnostic_thread_id_t thread_id
)
1407 sarif_thread_flow
**slot
= m_thread_id_map
.get (thread_id
);
1411 unsigned next_thread_flow_idx
= m_thread_flows_arr
->size ();
1412 auto thread_flow_obj
1413 = ::make_unique
<sarif_thread_flow
> (*this, thread
, next_thread_flow_idx
);
1414 m_thread_id_map
.put (thread_id
, thread_flow_obj
.get ()); // borrowed
1415 sarif_thread_flow
*result
= thread_flow_obj
.get ();
1416 m_thread_flows_arr
->append
<sarif_thread_flow
> (std::move (thread_flow_obj
));
1421 sarif_code_flow::get_thread_flow (diagnostic_thread_id_t thread_id
)
1423 sarif_thread_flow
**slot
= m_thread_id_map
.get (thread_id
);
1424 gcc_assert (slot
); // it must already have one
1429 sarif_code_flow::add_location (sarif_thread_flow_location
&tfl_obj
)
1431 m_all_tfl_objs
.push_back (&tfl_obj
);
1434 sarif_thread_flow_location
&
1435 sarif_code_flow::get_thread_flow_loc_obj (diagnostic_event_id_t event_id
) const
1437 gcc_assert (event_id
.known_p ());
1438 gcc_assert ((size_t)event_id
.zero_based () < m_all_tfl_objs
.size ());
1439 sarif_thread_flow_location
*tfl_obj
= m_all_tfl_objs
[event_id
.zero_based ()];
1440 gcc_assert (tfl_obj
);
1444 /* class sarif_thread_flow : public sarif_object. */
1446 sarif_thread_flow::sarif_thread_flow (sarif_code_flow
&parent
,
1447 const diagnostic_thread
&thread
,
1448 unsigned idx_within_parent
)
1449 : m_parent (parent
),
1450 m_idx_within_parent (idx_within_parent
)
1452 /* "id" property (SARIF v2.1.0 section 3.37.2). */
1453 label_text
name (thread
.get_name (false));
1454 set_string ("id", name
.get ());
1456 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
1457 m_locations_arr
= new json::array ();
1459 /* Give ownership of m_locations_arr to json::object;
1460 keep a borrowed ptr. */
1461 set ("locations", m_locations_arr
);
1464 /* Add a sarif_thread_flow_location to this threadFlow object, but
1465 don't populate it yet. */
1467 sarif_thread_flow_location
&
1468 sarif_thread_flow::add_location ()
1470 const unsigned thread_flow_location_idx
= m_locations_arr
->size ();
1471 sarif_thread_flow_location
*thread_flow_loc_obj
1472 = new sarif_thread_flow_location (*this, thread_flow_location_idx
);
1473 m_locations_arr
->append (thread_flow_loc_obj
);
1474 m_parent
.add_location (*thread_flow_loc_obj
);
1475 return *thread_flow_loc_obj
;
1478 /* class sarif_builder. */
1480 /* sarif_builder's ctor. */
1482 sarif_builder::sarif_builder (diagnostic_context
&context
,
1483 const line_maps
*line_maps
,
1484 const char *main_input_filename_
,
1486 enum sarif_version version
)
1487 : m_context (context
),
1488 m_printer (context
.m_printer
),
1489 m_line_maps (line_maps
),
1490 m_token_printer (*this),
1491 m_version (version
),
1493 (::make_unique
<sarif_invocation
> (*this,
1494 context
.get_original_argv ())),
1495 m_results_array (new json::array ()),
1496 m_cur_group_result (nullptr),
1497 m_seen_any_relative_paths (false),
1499 m_rules_arr (new json::array ()),
1500 m_tabstop (context
.m_tabstop
),
1501 m_formatted (formatted
),
1502 m_next_result_idx (0),
1503 m_current_code_flow (nullptr)
1505 gcc_assert (m_line_maps
);
1507 /* Mark MAIN_INPUT_FILENAME_ as the artifact that the tool was
1509 Only quote the contents if it gets referenced by physical locations,
1510 since otherwise the "no diagnostics" case would quote the main input
1511 file, and doing so noticeably bloated the output seen in analyzer
1512 integration testing (build directory went from 20G -> 21G). */
1513 if (main_input_filename_
)
1514 get_or_create_artifact (main_input_filename_
,
1515 diagnostic_artifact_role::analysis_target
,
1519 sarif_builder::~sarif_builder ()
1521 /* Normally m_filename_to_artifact_map will have been emptied as part
1522 of make_run_object, but this isn't run by all the selftests.
1523 Ensure the artifact objects are cleaned up for such cases. */
1524 for (auto iter
: m_filename_to_artifact_map
)
1526 sarif_artifact
*artifact_obj
= iter
.second
;
1527 delete artifact_obj
;
1531 /* Implementation of "on_report_diagnostic" for SARIF output. */
1534 sarif_builder::on_report_diagnostic (const diagnostic_info
&diagnostic
,
1535 diagnostic_t orig_diag_kind
)
1537 pp_output_formatted_text (m_printer
, m_context
.get_urlifier ());
1539 if (diagnostic
.kind
== DK_ICE
|| diagnostic
.kind
== DK_ICE_NOBT
)
1541 m_invocation_obj
->add_notification_for_ice (diagnostic
, *this);
1545 if (m_cur_group_result
)
1546 /* Nested diagnostic. */
1547 m_cur_group_result
->on_nested_diagnostic (diagnostic
,
1552 /* Top-level diagnostic. */
1553 m_cur_group_result
= make_result_object (diagnostic
, orig_diag_kind
,
1554 m_next_result_idx
++);
1558 /* Implementation of diagnostic_context::m_diagrams.m_emission_cb
1559 for SARIF output. */
1562 sarif_builder::emit_diagram (const diagnostic_diagram
&diagram
)
1564 /* We must be within the emission of a top-level diagnostic. */
1565 gcc_assert (m_cur_group_result
);
1566 m_cur_group_result
->on_diagram (diagram
, *this);
1569 /* Implementation of "end_group_cb" for SARIF output. */
1572 sarif_builder::end_group ()
1574 if (m_cur_group_result
)
1576 m_cur_group_result
->process_worklist (*this);
1577 m_results_array
->append
<sarif_result
> (std::move (m_cur_group_result
));
1581 /* Create a top-level object, and add it to all the results
1582 (and other entities) we've seen so far, moving ownership
1585 std::unique_ptr
<sarif_log
>
1586 sarif_builder::flush_to_object ()
1588 m_invocation_obj
->prepare_to_flush (*this);
1589 std::unique_ptr
<sarif_log
> top
1590 = make_top_level_object (std::move (m_invocation_obj
),
1591 std::move (m_results_array
));
1595 /* Create a top-level object, and add it to all the results
1596 (and other entities) we've seen so far.
1598 Flush it all to OUTF. */
1601 sarif_builder::flush_to_file (FILE *outf
)
1603 std::unique_ptr
<sarif_log
> top
= flush_to_object ();
1604 top
->dump (outf
, m_formatted
);
1605 fprintf (outf
, "\n");
1608 /* Attempt to convert DIAG_KIND to a suitable value for the "level"
1609 property (SARIF v2.1.0 section 3.27.10).
1611 Return nullptr if there isn't one. */
1614 maybe_get_sarif_level (diagnostic_t diag_kind
)
1623 case DK_ANACHRONISM
:
1630 /* Make a string for DIAG_KIND suitable for use a ruleId
1631 (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
1632 have anything better to use. */
1635 make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind
)
1637 /* Lose the trailing ": ". */
1638 const char *kind_text
= get_diagnostic_kind_text (diag_kind
);
1639 size_t len
= strlen (kind_text
);
1640 gcc_assert (len
> 2);
1641 gcc_assert (kind_text
[len
- 2] == ':');
1642 gcc_assert (kind_text
[len
- 1] == ' ');
1643 char *rstrip
= xstrdup (kind_text
);
1644 rstrip
[len
- 2] = '\0';
1648 /* Make a "result" object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
1650 std::unique_ptr
<sarif_result
>
1651 sarif_builder::make_result_object (const diagnostic_info
&diagnostic
,
1652 diagnostic_t orig_diag_kind
,
1653 unsigned idx_within_parent
)
1655 auto result_obj
= ::make_unique
<sarif_result
> (idx_within_parent
);
1657 /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
1658 /* Ideally we'd have an option_name for these. */
1659 if (char *option_text
1660 = m_context
.make_option_name (diagnostic
.option_id
,
1661 orig_diag_kind
, diagnostic
.kind
))
1663 /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
1664 Set ruleId referencing them. */
1665 result_obj
->set_string ("ruleId", option_text
);
1666 if (m_rule_id_set
.contains (option_text
))
1670 /* This is the first time we've seen this ruleId. */
1671 /* Add to set, taking ownership. */
1672 m_rule_id_set
.add (option_text
);
1674 m_rules_arr
->append
<sarif_reporting_descriptor
>
1675 (make_reporting_descriptor_object_for_warning (diagnostic
,
1682 /* Otherwise, we have an "error" or a stray "note"; use the
1683 diagnostic kind as the ruleId, so that the result object at least
1685 We don't bother creating reportingDescriptor objects for these. */
1686 char *rule_id
= make_rule_id_for_diagnostic_kind (orig_diag_kind
);
1687 result_obj
->set_string ("ruleId", rule_id
);
1691 if (diagnostic
.metadata
)
1693 /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
1694 if (int cwe_id
= diagnostic
.metadata
->get_cwe ())
1696 auto taxa_arr
= ::make_unique
<json::array
> ();
1697 taxa_arr
->append
<sarif_reporting_descriptor_reference
>
1698 (make_reporting_descriptor_reference_object_for_cwe_id (cwe_id
));
1699 result_obj
->set
<json::array
> ("taxa", std::move (taxa_arr
));
1702 diagnostic
.metadata
->maybe_add_sarif_properties (*result_obj
);
1704 /* We don't yet support diagnostic_metadata::rule. */
1707 /* "level" property (SARIF v2.1.0 section 3.27.10). */
1708 if (const char *sarif_level
= maybe_get_sarif_level (diagnostic
.kind
))
1709 result_obj
->set_string ("level", sarif_level
);
1711 /* "message" property (SARIF v2.1.0 section 3.27.11). */
1713 = make_message_object (pp_formatted_text (m_printer
));
1714 pp_clear_output_area (m_printer
);
1715 result_obj
->set
<sarif_message
> ("message", std::move (message_obj
));
1717 /* "locations" property (SARIF v2.1.0 section 3.27.12). */
1718 result_obj
->set
<json::array
>
1720 make_locations_arr (*result_obj
.get (),
1722 diagnostic_artifact_role::result_file
));
1724 /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
1725 if (const diagnostic_path
*path
= diagnostic
.richloc
->get_path ())
1727 auto code_flows_arr
= ::make_unique
<json::array
> ();
1728 const unsigned code_flow_index
= 0;
1729 code_flows_arr
->append
<sarif_code_flow
>
1730 (make_code_flow_object (*result_obj
.get (),
1733 result_obj
->set
<json::array
> ("codeFlows", std::move (code_flows_arr
));
1736 /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
1737 set up later, if any nested diagnostics occur within this diagnostic
1740 /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
1741 const rich_location
*richloc
= diagnostic
.richloc
;
1742 if (richloc
->get_num_fixit_hints ())
1744 auto fix_arr
= ::make_unique
<json::array
> ();
1745 fix_arr
->append
<sarif_fix
> (make_fix_object (*richloc
));
1746 result_obj
->set
<json::array
> ("fixes", std::move (fix_arr
));
1752 /* Make a "reportingDescriptor" object (SARIF v2.1.0 section 3.49)
1753 for a GCC warning. */
1755 std::unique_ptr
<sarif_reporting_descriptor
>
1757 make_reporting_descriptor_object_for_warning (const diagnostic_info
&diagnostic
,
1758 diagnostic_t
/*orig_diag_kind*/,
1759 const char *option_text
)
1761 auto reporting_desc
= ::make_unique
<sarif_reporting_descriptor
> ();
1763 /* "id" property (SARIF v2.1.0 section 3.49.3). */
1764 reporting_desc
->set_string ("id", option_text
);
1766 /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
1767 it seems redundant compared to "id". */
1769 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
1770 if (char *option_url
= m_context
.make_option_url (diagnostic
.option_id
))
1772 reporting_desc
->set_string ("helpUri", option_url
);
1776 return reporting_desc
;
1779 /* Make a "reportingDescriptor" object (SARIF v2.1.0 section 3.49)
1780 for CWE_ID, for use within the CWE taxa array. */
1782 std::unique_ptr
<sarif_reporting_descriptor
>
1783 sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id
) const
1785 auto reporting_desc
= ::make_unique
<sarif_reporting_descriptor
> ();
1787 /* "id" property (SARIF v2.1.0 section 3.49.3). */
1790 pp_printf (&pp
, "%i", cwe_id
);
1791 reporting_desc
->set_string ("id", pp_formatted_text (&pp
));
1794 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
1796 char *url
= get_cwe_url (cwe_id
);
1797 reporting_desc
->set_string ("helpUri", url
);
1801 return reporting_desc
;
1804 /* Make a "reportingDescriptorReference" object (SARIF v2.1.0 section 3.52)
1805 referencing CWE_ID, for use within a result object.
1806 Also, add CWE_ID to m_cwe_id_set. */
1808 std::unique_ptr
<sarif_reporting_descriptor_reference
>
1810 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id
)
1812 auto desc_ref_obj
= ::make_unique
<sarif_reporting_descriptor_reference
> ();
1814 /* "id" property (SARIF v2.1.0 section 3.52.4). */
1817 pp_printf (&pp
, "%i", cwe_id
);
1818 desc_ref_obj
->set_string ("id", pp_formatted_text (&pp
));
1821 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
1822 desc_ref_obj
->set
<sarif_tool_component_reference
>
1823 ("toolComponent", make_tool_component_reference_object_for_cwe ());
1825 /* Add CWE_ID to our set. */
1826 gcc_assert (cwe_id
> 0);
1827 m_cwe_id_set
.add (cwe_id
);
1829 return desc_ref_obj
;
1832 /* Make a "toolComponentReference" object (SARIF v2.1.0 section 3.54) that
1833 references the CWE taxonomy. */
1835 std::unique_ptr
<sarif_tool_component_reference
>
1837 make_tool_component_reference_object_for_cwe () const
1839 auto comp_ref_obj
= ::make_unique
<sarif_tool_component_reference
> ();
1841 /* "name" property (SARIF v2.1.0 section 3.54.3). */
1842 comp_ref_obj
->set_string ("name", "cwe");
1844 return comp_ref_obj
;
1847 /* Make an array suitable for use as the "locations" property of:
1848 - a "result" object (SARIF v2.1.0 section 3.27.12), or
1849 - a "notification" object (SARIF v2.1.0 section 3.58.4).
1850 Use LOC_MGR for any locations that need "id" values. */
1852 std::unique_ptr
<json::array
>
1853 sarif_builder::make_locations_arr (sarif_location_manager
&loc_mgr
,
1854 const diagnostic_info
&diagnostic
,
1855 enum diagnostic_artifact_role role
)
1857 auto locations_arr
= ::make_unique
<json::array
> ();
1858 const logical_location
*logical_loc
= nullptr;
1859 if (auto client_data_hooks
= m_context
.get_client_data_hooks ())
1860 logical_loc
= client_data_hooks
->get_current_logical_location ();
1863 = make_location_object (loc_mgr
, *diagnostic
.richloc
, logical_loc
, role
);
1864 /* Don't add entirely empty location objects to the array. */
1865 if (!location_obj
->is_empty ())
1866 locations_arr
->append
<sarif_location
> (std::move (location_obj
));
1868 return locations_arr
;
1871 /* If LOGICAL_LOC is non-null, use it to create a "logicalLocations" property
1872 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
1876 set_any_logical_locs_arr (sarif_location
&location_obj
,
1877 const logical_location
*logical_loc
)
1881 auto location_locs_arr
= ::make_unique
<json::array
> ();
1882 location_locs_arr
->append
<sarif_logical_location
>
1883 (make_sarif_logical_location_object (*logical_loc
));
1884 location_obj
.set
<json::array
> ("logicalLocations",
1885 std::move (location_locs_arr
));
1888 /* Make a "location" object (SARIF v2.1.0 section 3.28) for RICH_LOC
1890 Use LOC_MGR for any locations that need "id" values, and for
1891 any worklist items. */
1893 std::unique_ptr
<sarif_location
>
1894 sarif_builder::make_location_object (sarif_location_manager
&loc_mgr
,
1895 const rich_location
&rich_loc
,
1896 const logical_location
*logical_loc
,
1897 enum diagnostic_artifact_role role
)
1899 class escape_nonascii_renderer
: public content_renderer
1902 escape_nonascii_renderer (const rich_location
&richloc
,
1903 enum diagnostics_escape_format escape_format
)
1904 : m_richloc (richloc
),
1905 m_escape_format (escape_format
)
1908 std::unique_ptr
<sarif_multiformat_message_string
>
1909 render (const sarif_builder
&builder
) const final override
1911 diagnostic_context dc
;
1912 diagnostic_initialize (&dc
, 0);
1913 dc
.m_source_printing
.enabled
= true;
1914 dc
.m_source_printing
.colorize_source_p
= false;
1915 dc
.m_source_printing
.show_labels_p
= true;
1916 dc
.m_source_printing
.show_line_numbers_p
= true;
1918 rich_location
my_rich_loc (m_richloc
);
1919 my_rich_loc
.set_escape_on_output (true);
1921 diagnostic_source_print_policy
source_policy (dc
);
1922 dc
.set_escape_format (m_escape_format
);
1923 source_policy
.print (*dc
.m_printer
, my_rich_loc
, DK_ERROR
, nullptr);
1925 std::unique_ptr
<sarif_multiformat_message_string
> result
1926 = builder
.make_multiformat_message_string
1927 (pp_formatted_text (dc
.m_printer
));
1929 diagnostic_finish (&dc
);
1934 const rich_location
&m_richloc
;
1935 enum diagnostics_escape_format m_escape_format
;
1936 } the_renderer (rich_loc
,
1937 m_context
.get_escape_format ());
1939 auto location_obj
= ::make_unique
<sarif_location
> ();
1941 /* Get primary loc from RICH_LOC. */
1942 location_t loc
= rich_loc
.get_loc ();
1944 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
1945 const content_renderer
*snippet_renderer
1946 = rich_loc
.escape_on_output_p () ? &the_renderer
: nullptr;
1947 if (auto phs_loc_obj
1948 = maybe_make_physical_location_object (loc
, role
,
1949 rich_loc
.get_column_override (),
1951 location_obj
->set
<sarif_physical_location
> ("physicalLocation",
1952 std::move (phs_loc_obj
));
1954 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
1955 set_any_logical_locs_arr (*location_obj
, logical_loc
);
1957 /* Handle labelled ranges and/or secondary locations. */
1959 std::unique_ptr
<json::array
> annotations_arr
= nullptr;
1960 for (unsigned int i
= 0; i
< rich_loc
.get_num_locations (); i
++)
1962 const location_range
*range
= rich_loc
.get_range (i
);
1963 bool handled
= false;
1964 if (const range_label
*label
= range
->m_label
)
1966 label_text text
= label
->get_text (i
);
1969 /* Create annotations for any labelled ranges. */
1970 location_t range_loc
= rich_loc
.get_loc (i
);
1972 = maybe_make_region_object (range_loc
,
1973 rich_loc
.get_column_override ());
1976 if (!annotations_arr
)
1977 annotations_arr
= ::make_unique
<json::array
> ();
1978 region
->set
<sarif_message
>
1979 ("message", make_message_object (text
.get ()));
1980 annotations_arr
->append
<sarif_region
> (std::move (region
));
1986 /* Add related locations for any secondary locations in RICH_LOC
1987 that don't have labels (and thus aren't added to "annotations"). */
1988 if (i
> 0 && !handled
)
1989 loc_mgr
.add_relationship_to_worklist
1990 (*location_obj
.get (),
1991 sarif_location_manager::worklist_item::kind::unlabelled_secondary_location
,
1994 if (annotations_arr
)
1995 /* "annotations" property (SARIF v2.1.0 section 3.28.6). */
1996 location_obj
->set
<json::array
> ("annotations",
1997 std::move (annotations_arr
));
2000 add_any_include_chain (loc_mgr
, *location_obj
.get (), loc
);
2002 /* A flag for hinting that the diagnostic involves issues at the
2003 level of character encodings (such as homoglyphs, or misleading
2004 bidirectional control codes), and thus that it will be helpful
2005 to the user if we show some representation of
2006 how the characters in the pertinent source lines are encoded. */
2007 if (rich_loc
.escape_on_output_p ())
2009 sarif_property_bag
&bag
= location_obj
->get_or_create_properties ();
2010 bag
.set_bool ("gcc/escapeNonAscii", rich_loc
.escape_on_output_p ());
2013 return location_obj
;
2016 /* If WHERE was #included from somewhere, add a worklist item
2017 to LOC_MGR to lazily add a location for the #include location,
2018 and relationships between it and the LOCATION_OBJ.
2019 Compare with diagnostic_context::report_current_module, but rather
2020 than iterating the current chain, we add the next edge and iterate
2021 in the worklist, so that edges are only added once. */
2024 sarif_builder::add_any_include_chain (sarif_location_manager
&loc_mgr
,
2025 sarif_location
&location_obj
,
2028 if (where
<= BUILTINS_LOCATION
)
2031 const line_map_ordinary
*map
= nullptr;
2032 linemap_resolve_location (m_line_maps
, where
,
2033 LRK_MACRO_DEFINITION_LOCATION
,
2039 location_t include_loc
= linemap_included_from (map
);
2040 map
= linemap_included_from_linemap (m_line_maps
, map
);
2043 loc_mgr
.add_relationship_to_worklist
2045 sarif_result::worklist_item::kind::included_from
,
2049 /* Make a "location" object (SARIF v2.1.0 section 3.28) for WHERE
2050 within an include chain. */
2052 std::unique_ptr
<sarif_location
>
2053 sarif_builder::make_location_object (sarif_location_manager
&loc_mgr
,
2055 enum diagnostic_artifact_role role
)
2057 auto location_obj
= ::make_unique
<sarif_location
> ();
2059 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
2060 if (auto phs_loc_obj
2061 = maybe_make_physical_location_object (loc
, role
, 0, nullptr))
2062 location_obj
->set
<sarif_physical_location
> ("physicalLocation",
2063 std::move (phs_loc_obj
));
2065 add_any_include_chain (loc_mgr
, *location_obj
.get (), loc
);
2067 return location_obj
;
2070 /* Make a "location" object (SARIF v2.1.0 section 3.28) for EVENT
2071 within a diagnostic_path. */
2073 std::unique_ptr
<sarif_location
>
2074 sarif_builder::make_location_object (sarif_location_manager
&loc_mgr
,
2075 const diagnostic_event
&event
,
2076 enum diagnostic_artifact_role role
)
2078 auto location_obj
= ::make_unique
<sarif_location
> ();
2080 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
2081 location_t loc
= event
.get_location ();
2082 if (auto phs_loc_obj
2083 = maybe_make_physical_location_object (loc
, role
, 0, nullptr))
2084 location_obj
->set
<sarif_physical_location
> ("physicalLocation",
2085 std::move (phs_loc_obj
));
2087 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
2088 const logical_location
*logical_loc
= event
.get_logical_location ();
2089 set_any_logical_locs_arr (*location_obj
, logical_loc
);
2091 /* "message" property (SARIF v2.1.0 section 3.28.5). */
2092 label_text ev_desc
= event
.get_desc (false);
2093 location_obj
->set
<sarif_message
> ("message",
2094 make_message_object (ev_desc
.get ()));
2096 add_any_include_chain (loc_mgr
, *location_obj
.get (), loc
);
2098 return location_obj
;
2101 /* Make a "physicalLocation" object (SARIF v2.1.0 section 3.29) for LOC.
2103 If COLUMN_OVERRIDE is non-zero, then use it as the column number
2104 if LOC has no column information.
2106 Ensure that we have an artifact object for the file, adding ROLE to it,
2107 and flagging that we will attempt to embed the contents of the artifact
2108 when writing it out. */
2110 std::unique_ptr
<sarif_physical_location
>
2112 maybe_make_physical_location_object (location_t loc
,
2113 enum diagnostic_artifact_role role
,
2114 int column_override
,
2115 const content_renderer
*snippet_renderer
)
2117 if (loc
<= BUILTINS_LOCATION
|| LOCATION_FILE (loc
) == nullptr)
2120 auto phys_loc_obj
= ::make_unique
<sarif_physical_location
> ();
2122 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
2123 phys_loc_obj
->set
<sarif_artifact_location
>
2124 ("artifactLocation", make_artifact_location_object (loc
));
2125 get_or_create_artifact (LOCATION_FILE (loc
), role
, true);
2127 /* "region" property (SARIF v2.1.0 section 3.29.4). */
2128 if (auto region_obj
= maybe_make_region_object (loc
, column_override
))
2129 phys_loc_obj
->set
<sarif_region
> ("region", std::move (region_obj
));
2131 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
2132 if (auto context_region_obj
2133 = maybe_make_region_object_for_context (loc
, snippet_renderer
))
2134 phys_loc_obj
->set
<sarif_region
> ("contextRegion",
2135 std::move (context_region_obj
));
2137 /* Instead, we add artifacts to the run as a whole,
2138 with artifact.contents.
2139 Could do both, though. */
2141 return phys_loc_obj
;
2144 /* Make an "artifactLocation" object (SARIF v2.1.0 section 3.4) for LOC,
2145 or return nullptr. */
2147 std::unique_ptr
<sarif_artifact_location
>
2148 sarif_builder::make_artifact_location_object (location_t loc
)
2150 return make_artifact_location_object (LOCATION_FILE (loc
));
2153 /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
2154 for when we need to express paths relative to PWD. */
2156 #define PWD_PROPERTY_NAME ("PWD")
2158 /* Make an "artifactLocation" object (SARIF v2.1.0 section 3.4) for FILENAME,
2159 or return nullptr. */
2161 std::unique_ptr
<sarif_artifact_location
>
2162 sarif_builder::make_artifact_location_object (const char *filename
)
2164 auto artifact_loc_obj
= ::make_unique
<sarif_artifact_location
> ();
2166 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
2167 artifact_loc_obj
->set_string ("uri", filename
);
2169 if (filename
[0] != '/')
2171 /* If we have a relative path, set the "uriBaseId" property
2172 (SARIF v2.1.0 section 3.4.4). */
2173 artifact_loc_obj
->set_string ("uriBaseId", PWD_PROPERTY_NAME
);
2174 m_seen_any_relative_paths
= true;
2177 return artifact_loc_obj
;
2180 /* Get the PWD, or nullptr, as an absolute file-based URI,
2181 adding a trailing forward slash (as required by SARIF v2.1.0
2182 section 3.14.14). */
2187 /* The prefix of a file-based URI, up to, but not including the path. */
2188 #define FILE_PREFIX ("file://")
2190 const char *pwd
= getpwd ();
2193 size_t len
= strlen (pwd
);
2194 if (len
== 0 || pwd
[len
- 1] != '/')
2195 return concat (FILE_PREFIX
, pwd
, "/", nullptr);
2198 gcc_assert (pwd
[len
- 1] == '/');
2199 return concat (FILE_PREFIX
, pwd
, nullptr);
2203 /* Make an "artifactLocation" object (SARIF v2.1.0 section 3.4) for the pwd,
2204 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
2205 section 3.14.14) when we have any relative paths. */
2207 std::unique_ptr
<sarif_artifact_location
>
2208 sarif_builder::make_artifact_location_object_for_pwd () const
2210 auto artifact_loc_obj
= ::make_unique
<sarif_artifact_location
> ();
2212 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
2213 if (char *pwd
= make_pwd_uri_str ())
2215 gcc_assert (strlen (pwd
) > 0);
2216 gcc_assert (pwd
[strlen (pwd
) - 1] == '/');
2217 artifact_loc_obj
->set_string ("uri", pwd
);
2221 return artifact_loc_obj
;
2224 /* Get the column number within EXPLOC. */
2227 sarif_builder::get_sarif_column (expanded_location exploc
) const
2229 cpp_char_column_policy
policy (m_tabstop
, cpp_wcwidth
);
2230 return location_compute_display_column (m_context
.get_file_cache (),
2234 /* Make a "region" object (SARIF v2.1.0 section 3.30) for LOC,
2237 If COLUMN_OVERRIDE is non-zero, then use it as the column number
2238 if LOC has no column information.
2240 We only support text properties of regions ("text regions"),
2241 not binary properties ("binary regions"); see 3.30.1. */
2243 std::unique_ptr
<sarif_region
>
2244 sarif_builder::maybe_make_region_object (location_t loc
,
2245 int column_override
) const
2247 location_t caret_loc
= get_pure_location (loc
);
2249 if (caret_loc
<= BUILTINS_LOCATION
)
2252 location_t start_loc
= get_start (loc
);
2253 location_t finish_loc
= get_finish (loc
);
2255 expanded_location exploc_caret
= expand_location (caret_loc
);
2256 expanded_location exploc_start
= expand_location (start_loc
);
2257 expanded_location exploc_finish
= expand_location (finish_loc
);
2259 if (exploc_start
.file
!=exploc_caret
.file
)
2261 if (exploc_finish
.file
!=exploc_caret
.file
)
2264 /* We can have line == 0 in the presence of "#" lines.
2265 SARIF requires lines > 0, so if we hit this case we don't have a
2266 way of validly representing the region as SARIF; bail out. */
2267 if (exploc_start
.line
<= 0)
2270 auto region_obj
= ::make_unique
<sarif_region
> ();
2272 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
2273 region_obj
->set_integer ("startLine", exploc_start
.line
);
2275 /* "startColumn" property (SARIF v2.1.0 section 3.30.6).
2277 We use column == 0 to mean the whole line, so omit the column
2278 information for this case, unless COLUMN_OVERRIDE is non-zero,
2279 (for handling certain awkward lexer diagnostics) */
2281 if (exploc_start
.column
== 0 && column_override
)
2282 /* Use the provided column number. */
2283 exploc_start
.column
= column_override
;
2285 if (exploc_start
.column
> 0)
2287 int start_column
= get_sarif_column (exploc_start
);
2288 region_obj
->set_integer ("startColumn", start_column
);
2291 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
2292 if (exploc_finish
.line
!= exploc_start
.line
2293 && exploc_finish
.line
> 0)
2294 region_obj
->set_integer ("endLine", exploc_finish
.line
);
2296 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
2297 This expresses the column immediately beyond the range.
2299 We use column == 0 to mean the whole line, so omit the column
2300 information for this case. */
2301 if (exploc_finish
.column
> 0)
2303 int next_column
= get_sarif_column (exploc_finish
) + 1;
2304 region_obj
->set_integer ("endColumn", next_column
);
2310 /* Make a "region" object (SARIF v2.1.0 section 3.30) for the "contextRegion"
2311 property (SARIF v2.1.0 section 3.29.5) of a "physicalLocation".
2313 This is similar to maybe_make_region_object, but ignores column numbers,
2314 covering the line(s) as a whole, and including a "snippet" property
2315 embedding those source lines, making it easier for consumers to show
2316 the pertinent source. */
2318 std::unique_ptr
<sarif_region
>
2320 maybe_make_region_object_for_context (location_t loc
,
2321 const content_renderer
*snippet_renderer
)
2324 location_t caret_loc
= get_pure_location (loc
);
2326 if (caret_loc
<= BUILTINS_LOCATION
)
2329 location_t start_loc
= get_start (loc
);
2330 location_t finish_loc
= get_finish (loc
);
2332 expanded_location exploc_caret
= expand_location (caret_loc
);
2333 expanded_location exploc_start
= expand_location (start_loc
);
2334 expanded_location exploc_finish
= expand_location (finish_loc
);
2336 if (exploc_start
.file
!=exploc_caret
.file
)
2338 if (exploc_finish
.file
!=exploc_caret
.file
)
2341 /* We can have line == 0 in the presence of "#" lines.
2342 SARIF requires lines > 0, so if we hit this case we don't have a
2343 way of validly representing the region as SARIF; bail out. */
2344 if (exploc_start
.line
<= 0)
2347 auto region_obj
= ::make_unique
<sarif_region
> ();
2349 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
2350 region_obj
->set_integer ("startLine", exploc_start
.line
);
2352 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
2353 if (exploc_finish
.line
!= exploc_start
.line
2354 && exploc_finish
.line
> 0)
2355 region_obj
->set_integer ("endLine", exploc_finish
.line
);
2357 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
2358 if (auto artifact_content_obj
2359 = maybe_make_artifact_content_object (exploc_start
.file
,
2363 region_obj
->set
<sarif_artifact_content
> ("snippet",
2364 std::move (artifact_content_obj
));
2369 /* Make a "region" object (SARIF v2.1.0 section 3.30) for the deletion region
2370 of HINT (as per SARIF v2.1.0 section 3.57.3). */
2372 std::unique_ptr
<sarif_region
>
2373 sarif_builder::make_region_object_for_hint (const fixit_hint
&hint
) const
2375 location_t start_loc
= hint
.get_start_loc ();
2376 location_t next_loc
= hint
.get_next_loc ();
2378 expanded_location exploc_start
= expand_location (start_loc
);
2379 expanded_location exploc_next
= expand_location (next_loc
);
2381 auto region_obj
= ::make_unique
<sarif_region
> ();
2383 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
2384 region_obj
->set_integer ("startLine", exploc_start
.line
);
2386 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
2387 int start_col
= get_sarif_column (exploc_start
);
2388 region_obj
->set_integer ("startColumn", start_col
);
2390 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
2391 if (exploc_next
.line
!= exploc_start
.line
)
2392 region_obj
->set_integer ("endLine", exploc_next
.line
);
2394 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
2395 This expresses the column immediately beyond the range. */
2396 int next_col
= get_sarif_column (exploc_next
);
2397 region_obj
->set_integer ("endColumn", next_col
);
2402 /* Attempt to get a string for a logicalLocation's "kind" property
2403 (SARIF v2.1.0 section 3.33.7).
2404 Return nullptr if unknown. */
2407 maybe_get_sarif_kind (enum logical_location_kind kind
)
2413 case LOGICAL_LOCATION_KIND_UNKNOWN
:
2416 case LOGICAL_LOCATION_KIND_FUNCTION
:
2418 case LOGICAL_LOCATION_KIND_MEMBER
:
2420 case LOGICAL_LOCATION_KIND_MODULE
:
2422 case LOGICAL_LOCATION_KIND_NAMESPACE
:
2424 case LOGICAL_LOCATION_KIND_TYPE
:
2426 case LOGICAL_LOCATION_KIND_RETURN_TYPE
:
2427 return "returnType";
2428 case LOGICAL_LOCATION_KIND_PARAMETER
:
2430 case LOGICAL_LOCATION_KIND_VARIABLE
:
2435 /* Make a "logicalLocation" object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
2436 or return nullptr. */
2438 std::unique_ptr
<sarif_logical_location
>
2439 make_sarif_logical_location_object (const logical_location
&logical_loc
)
2441 auto logical_loc_obj
= ::make_unique
<sarif_logical_location
> ();
2443 /* "name" property (SARIF v2.1.0 section 3.33.4). */
2444 if (const char *short_name
= logical_loc
.get_short_name ())
2445 logical_loc_obj
->set_string ("name", short_name
);
2447 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
2448 if (const char *name_with_scope
= logical_loc
.get_name_with_scope ())
2449 logical_loc_obj
->set_string ("fullyQualifiedName", name_with_scope
);
2451 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
2452 if (const char *internal_name
= logical_loc
.get_internal_name ())
2453 logical_loc_obj
->set_string ("decoratedName", internal_name
);
2455 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
2456 enum logical_location_kind kind
= logical_loc
.get_kind ();
2457 if (const char *sarif_kind_str
= maybe_get_sarif_kind (kind
))
2458 logical_loc_obj
->set_string ("kind", sarif_kind_str
);
2460 return logical_loc_obj
;
2464 make_sarif_url_for_event (const sarif_code_flow
*code_flow
,
2465 diagnostic_event_id_t event_id
)
2467 gcc_assert (event_id
.known_p ());
2470 return label_text ();
2472 const sarif_thread_flow_location
&tfl_obj
2473 = code_flow
->get_thread_flow_loc_obj (event_id
);
2474 const int location_idx
= tfl_obj
.get_index_within_parent ();
2476 const sarif_thread_flow
&thread_flow_obj
= tfl_obj
.get_parent ();
2477 const int thread_flow_idx
= thread_flow_obj
.get_index_within_parent ();
2479 const sarif_code_flow
&code_flow_obj
= thread_flow_obj
.get_parent ();
2480 const int code_flow_idx
= code_flow_obj
.get_index_within_parent ();
2482 const sarif_result
&result_obj
= code_flow_obj
.get_parent ();
2483 const int result_idx
= result_obj
.get_index_within_parent ();
2485 /* We only support a single run object in the log. */
2486 const int run_idx
= 0;
2488 char *buf
= xasprintf
2489 ("sarif:/runs/%i/results/%i/codeFlows/%i/threadFlows/%i/locations/%i",
2490 run_idx
, result_idx
, code_flow_idx
, thread_flow_idx
, location_idx
);
2491 return label_text::take (buf
);
2494 /* Make a "codeFlow" object (SARIF v2.1.0 section 3.36) for PATH. */
2496 std::unique_ptr
<sarif_code_flow
>
2497 sarif_builder::make_code_flow_object (sarif_result
&result
,
2498 unsigned idx_within_parent
,
2499 const diagnostic_path
&path
)
2502 = ::make_unique
<sarif_code_flow
> (result
, idx_within_parent
);
2505 Create threadFlows and threadFlowLocation objects within them,
2506 effectively recording a mapping from event_id to threadFlowLocation
2507 so that we can later go from an event_id to a URI within the
2509 for (unsigned i
= 0; i
< path
.num_events (); i
++)
2511 const diagnostic_event
&event
= path
.get_event (i
);
2512 const diagnostic_thread_id_t thread_id
= event
.get_thread_id ();
2514 sarif_thread_flow
&thread_flow_obj
2515 = code_flow_obj
->get_or_append_thread_flow (path
.get_thread (thread_id
),
2517 thread_flow_obj
.add_location ();
2520 /* Second pass: walk the events, populating the tfl objs. */
2521 m_current_code_flow
= code_flow_obj
.get ();
2522 for (unsigned i
= 0; i
< path
.num_events (); i
++)
2524 const diagnostic_event
&event
= path
.get_event (i
);
2525 sarif_thread_flow_location
&thread_flow_loc_obj
2526 = code_flow_obj
->get_thread_flow_loc_obj (i
);
2527 populate_thread_flow_location_object (result
,
2528 thread_flow_loc_obj
,
2532 m_current_code_flow
= nullptr;
2534 return code_flow_obj
;
2537 /* Populate TFL_OBJ, a "threadFlowLocation" object (SARIF v2.1.0 section 3.38)
2542 populate_thread_flow_location_object (sarif_result
&result
,
2543 sarif_thread_flow_location
&tfl_obj
,
2544 const diagnostic_event
&ev
,
2545 int event_execution_idx
)
2547 /* Give diagnostic_event subclasses a chance to add custom properties
2548 via a property bag. */
2549 ev
.maybe_add_sarif_properties (tfl_obj
);
2551 /* "location" property (SARIF v2.1.0 section 3.38.3). */
2552 tfl_obj
.set
<sarif_location
>
2554 make_location_object (result
, ev
, diagnostic_artifact_role::traced_file
));
2556 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
2557 diagnostic_event::meaning m
= ev
.get_meaning ();
2558 if (auto kinds_arr
= maybe_make_kinds_array (m
))
2559 tfl_obj
.set
<json::array
> ("kinds", std::move (kinds_arr
));
2561 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
2562 tfl_obj
.set_integer ("nestingLevel", ev
.get_stack_depth ());
2564 /* "executionOrder" property (SARIF v2.1.0 3.38.11).
2565 Offset by 1 to match the human-readable values emitted by %@. */
2566 tfl_obj
.set_integer ("executionOrder", event_execution_idx
+ 1);
2568 /* It might be nice to eventually implement the following for -fanalyzer:
2569 - the "stack" property (SARIF v2.1.0 section 3.38.5)
2570 - the "state" property (SARIF v2.1.0 section 3.38.9)
2571 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
2574 /* If M has any known meaning, make a json array suitable for the "kinds"
2575 property of a "threadFlowLocation" object (SARIF v2.1.0 section 3.38.8).
2577 Otherwise, return nullptr. */
2579 std::unique_ptr
<json::array
>
2580 sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m
) const
2582 if (m
.m_verb
== diagnostic_event::VERB_unknown
2583 && m
.m_noun
== diagnostic_event::NOUN_unknown
2584 && m
.m_property
== diagnostic_event::PROPERTY_unknown
)
2587 auto kinds_arr
= ::make_unique
<json::array
> ();
2588 if (const char *verb_str
2589 = diagnostic_event::meaning::maybe_get_verb_str (m
.m_verb
))
2590 kinds_arr
->append_string (verb_str
);
2591 if (const char *noun_str
2592 = diagnostic_event::meaning::maybe_get_noun_str (m
.m_noun
))
2593 kinds_arr
->append_string (noun_str
);
2594 if (const char *property_str
2595 = diagnostic_event::meaning::maybe_get_property_str (m
.m_property
))
2596 kinds_arr
->append_string (property_str
);
2600 /* Make a "message" object (SARIF v2.1.0 section 3.11) for MSG. */
2602 std::unique_ptr
<sarif_message
>
2603 sarif_builder::make_message_object (const char *msg
) const
2605 auto message_obj
= ::make_unique
<sarif_message
> ();
2607 /* "text" property (SARIF v2.1.0 section 3.11.8). */
2608 message_obj
->set_string ("text", msg
);
2613 /* Make a "message" object (SARIF v2.1.0 section 3.11) for DIAGRAM.
2614 We emit the diagram as a code block within the Markdown part
2617 std::unique_ptr
<sarif_message
>
2618 sarif_builder::make_message_object_for_diagram (const diagnostic_diagram
&diagram
)
2620 auto message_obj
= ::make_unique
<sarif_message
> ();
2622 /* "text" property (SARIF v2.1.0 section 3.11.8). */
2623 message_obj
->set_string ("text", diagram
.get_alt_text ());
2625 pretty_printer
*const pp
= m_printer
;
2626 char *saved_prefix
= pp_take_prefix (pp
);
2627 pp_set_prefix (pp
, nullptr);
2629 /* "To produce a code block in Markdown, simply indent every line of
2630 the block by at least 4 spaces or 1 tab."
2631 Here we use 4 spaces. */
2632 diagram
.get_canvas ().print_to_pp (pp
, " ");
2633 pp_set_prefix (pp
, saved_prefix
);
2635 /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
2636 message_obj
->set_string ("markdown", pp_formatted_text (pp
));
2638 pp_clear_output_area (pp
);
2643 /* Make a "multiformatMessageString object" (SARIF v2.1.0 section 3.12)
2646 std::unique_ptr
<sarif_multiformat_message_string
>
2647 sarif_builder::make_multiformat_message_string (const char *msg
) const
2649 auto message_obj
= ::make_unique
<sarif_multiformat_message_string
> ();
2651 /* "text" property (SARIF v2.1.0 section 3.12.3). */
2652 message_obj
->set_string ("text", msg
);
2657 /* Convert VERSION to a value for the "$schema" property
2658 of a "sarifLog" object (SARIF v2.1.0 section 3.13.3). */
2661 sarif_version_to_url (enum sarif_version version
)
2667 case sarif_version::v2_1_0
:
2668 return "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json";
2669 case sarif_version::v2_2_prerelease_2024_08_08
:
2670 return "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/refs/tags/2.2-prerelease-2024-08-08/sarif-2.2/schema/sarif-2-2.schema.json";
2674 /* Convert VERSION to a value for the "version" property
2675 of a "sarifLog" object (SARIF v2.1.0 section 3.13.2). */
2678 sarif_version_to_property (enum sarif_version version
)
2684 case sarif_version::v2_1_0
:
2686 case sarif_version::v2_2_prerelease_2024_08_08
:
2687 /* I would have used "2.2-prerelease-2024-08-08",
2688 but the schema only accepts "2.2". */
2693 /* Make a top-level "sarifLog" object (SARIF v2.1.0 section 3.13). */
2695 std::unique_ptr
<sarif_log
>
2697 make_top_level_object (std::unique_ptr
<sarif_invocation
> invocation_obj
,
2698 std::unique_ptr
<json::array
> results
)
2700 auto log_obj
= ::make_unique
<sarif_log
> ();
2702 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
2703 log_obj
->set_string ("$schema", sarif_version_to_url (m_version
));
2705 /* "version" property (SARIF v2.1.0 section 3.13.2). */
2706 log_obj
->set_string ("version", sarif_version_to_property (m_version
));
2708 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
2709 auto run_arr
= ::make_unique
<json::array
> ();
2710 auto run_obj
= make_run_object (std::move (invocation_obj
),
2711 std::move (results
));
2712 run_arr
->append
<sarif_run
> (std::move (run_obj
));
2713 log_obj
->set
<json::array
> ("runs", std::move (run_arr
));
2718 /* Make a "run" object (SARIF v2.1.0 section 3.14). */
2720 std::unique_ptr
<sarif_run
>
2722 make_run_object (std::unique_ptr
<sarif_invocation
> invocation_obj
,
2723 std::unique_ptr
<json::array
> results
)
2725 auto run_obj
= ::make_unique
<sarif_run
> ();
2727 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
2728 run_obj
->set
<sarif_tool
> ("tool", make_tool_object ());
2730 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
2731 if (auto taxonomies_arr
= maybe_make_taxonomies_array ())
2732 run_obj
->set
<json::array
> ("taxonomies", std::move (taxonomies_arr
));
2734 /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
2736 auto invocations_arr
= ::make_unique
<json::array
> ();
2737 invocations_arr
->append (std::move (invocation_obj
));
2738 run_obj
->set
<json::array
> ("invocations", std::move (invocations_arr
));
2741 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
2742 if (m_seen_any_relative_paths
)
2744 auto orig_uri_base_ids
= ::make_unique
<json::object
> ();
2745 orig_uri_base_ids
->set
<sarif_artifact_location
>
2746 (PWD_PROPERTY_NAME
, make_artifact_location_object_for_pwd ());
2747 run_obj
->set
<json::object
> ("originalUriBaseIds",
2748 std::move (orig_uri_base_ids
));
2751 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
2752 auto artifacts_arr
= ::make_unique
<json::array
> ();
2753 for (auto iter
: m_filename_to_artifact_map
)
2755 sarif_artifact
*artifact_obj
= iter
.second
;
2756 if (artifact_obj
->embed_contents_p ())
2757 artifact_obj
->populate_contents (*this);
2758 artifact_obj
->populate_roles ();
2759 artifacts_arr
->append (artifact_obj
);
2761 run_obj
->set
<json::array
> ("artifacts", std::move (artifacts_arr
));
2762 m_filename_to_artifact_map
.empty ();
2764 /* "results" property (SARIF v2.1.0 section 3.14.23). */
2765 run_obj
->set
<json::array
> ("results", std::move (results
));
2770 /* Make a "tool" object (SARIF v2.1.0 section 3.18). */
2772 std::unique_ptr
<sarif_tool
>
2773 sarif_builder::make_tool_object ()
2775 auto tool_obj
= ::make_unique
<sarif_tool
> ();
2777 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
2778 tool_obj
->set
<sarif_tool_component
> ("driver",
2779 make_driver_tool_component_object ());
2781 /* Report plugins via the "extensions" property
2782 (SARIF v2.1.0 section 3.18.3). */
2783 if (auto client_data_hooks
= m_context
.get_client_data_hooks ())
2784 if (const client_version_info
*vinfo
2785 = client_data_hooks
->get_any_version_info ())
2787 class my_plugin_visitor
: public client_version_info :: plugin_visitor
2790 void on_plugin (const diagnostic_client_plugin_info
&p
) final override
2792 /* Create a "toolComponent" object (SARIF v2.1.0 section 3.19)
2794 auto plugin_obj
= ::make_unique
<sarif_tool_component
> ();
2796 /* "name" property (SARIF v2.1.0 section 3.19.8). */
2797 if (const char *short_name
= p
.get_short_name ())
2798 plugin_obj
->set_string ("name", short_name
);
2800 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
2801 if (const char *full_name
= p
.get_full_name ())
2802 plugin_obj
->set_string ("fullName", full_name
);
2804 /* "version" property (SARIF v2.1.0 section 3.19.13). */
2805 if (const char *version
= p
.get_version ())
2806 plugin_obj
->set_string ("version", version
);
2808 m_plugin_objs
.push_back (std::move (plugin_obj
));
2810 std::vector
<std::unique_ptr
<sarif_tool_component
>> m_plugin_objs
;
2812 my_plugin_visitor v
;
2813 vinfo
->for_each_plugin (v
);
2814 if (v
.m_plugin_objs
.size () > 0)
2816 auto extensions_arr
= ::make_unique
<json::array
> ();
2817 for (auto &iter
: v
.m_plugin_objs
)
2818 extensions_arr
->append
<sarif_tool_component
> (std::move (iter
));
2819 tool_obj
->set
<json::array
> ("extensions",
2820 std::move (extensions_arr
));
2824 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
2825 "extensions" (see toplev.cc: print_version). */
2830 /* Make a "toolComponent" object (SARIF v2.1.0 section 3.19) for what SARIF
2831 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
2833 std::unique_ptr
<sarif_tool_component
>
2834 sarif_builder::make_driver_tool_component_object ()
2836 auto driver_obj
= ::make_unique
<sarif_tool_component
> ();
2838 if (auto client_data_hooks
= m_context
.get_client_data_hooks ())
2839 if (const client_version_info
*vinfo
2840 = client_data_hooks
->get_any_version_info ())
2842 /* "name" property (SARIF v2.1.0 section 3.19.8). */
2843 if (const char *name
= vinfo
->get_tool_name ())
2844 driver_obj
->set_string ("name", name
);
2846 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
2847 if (char *full_name
= vinfo
->maybe_make_full_name ())
2849 driver_obj
->set_string ("fullName", full_name
);
2853 /* "version" property (SARIF v2.1.0 section 3.19.13). */
2854 if (const char *version
= vinfo
->get_version_string ())
2855 driver_obj
->set_string ("version", version
);
2857 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
2858 if (char *version_url
= vinfo
->maybe_make_version_url ())
2860 driver_obj
->set_string ("informationUri", version_url
);
2865 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
2866 driver_obj
->set
<json::array
> ("rules", std::move (m_rules_arr
));
2871 /* If we've seen any CWE IDs, make an array for the "taxonomies" property
2872 (SARIF v2.1.0 section 3.14.8) of a run object, containing a single
2873 "toolComponent" (3.19) as per 3.19.3, representing the CWE.
2875 Otherwise return nullptr. */
2877 std::unique_ptr
<json::array
>
2878 sarif_builder::maybe_make_taxonomies_array () const
2880 auto cwe_obj
= maybe_make_cwe_taxonomy_object ();
2884 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
2885 auto taxonomies_arr
= ::make_unique
<json::array
> ();
2886 taxonomies_arr
->append
<sarif_tool_component
> (std::move (cwe_obj
));
2887 return taxonomies_arr
;
2890 /* If we've seen any CWE IDs, make a "toolComponent" object
2891 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
2892 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
2894 Otherwise return nullptr. */
2896 std::unique_ptr
<sarif_tool_component
>
2897 sarif_builder::maybe_make_cwe_taxonomy_object () const
2899 if (m_cwe_id_set
.is_empty ())
2902 auto taxonomy_obj
= ::make_unique
<sarif_tool_component
> ();
2904 /* "name" property (SARIF v2.1.0 section 3.19.8). */
2905 taxonomy_obj
->set_string ("name", "CWE");
2907 /* "version" property (SARIF v2.1.0 section 3.19.13). */
2908 taxonomy_obj
->set_string ("version", "4.7");
2910 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
2911 taxonomy_obj
->set_string ("organization", "MITRE");
2913 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
2914 taxonomy_obj
->set
<sarif_multiformat_message_string
>
2915 ("shortDescription",
2916 make_multiformat_message_string ("The MITRE"
2917 " Common Weakness Enumeration"));
2919 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
2920 auto taxa_arr
= ::make_unique
<json::array
> ();
2921 for (auto cwe_id
: m_cwe_id_set
)
2922 taxa_arr
->append
<sarif_reporting_descriptor
>
2923 (make_reporting_descriptor_object_for_cwe_id (cwe_id
));
2924 taxonomy_obj
->set
<json::array
> ("taxa", std::move (taxa_arr
));
2926 return taxonomy_obj
;
2929 /* Ensure that we have an "artifact" object (SARIF v2.1.0 section 3.24)
2930 for FILENAME, adding it to m_filename_to_artifact_map if not already
2931 found, and adding ROLE to it.
2932 If EMBED_CONTENTS is true, then flag that we will attempt to embed the
2933 contents of this artifact when writing it out. */
2936 sarif_builder::get_or_create_artifact (const char *filename
,
2937 enum diagnostic_artifact_role role
,
2938 bool embed_contents
)
2940 if (auto *slot
= m_filename_to_artifact_map
.get (filename
))
2942 (*slot
)->add_role (role
, embed_contents
);
2946 sarif_artifact
*artifact_obj
= new sarif_artifact (filename
);
2947 artifact_obj
->add_role (role
, embed_contents
);
2948 m_filename_to_artifact_map
.put (filename
, artifact_obj
);
2950 /* "location" property (SARIF v2.1.0 section 3.24.2). */
2951 artifact_obj
->set
<sarif_artifact_location
>
2952 ("location", make_artifact_location_object (filename
));
2954 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
2959 case diagnostic_artifact_role::analysis_target
:
2960 case diagnostic_artifact_role::result_file
:
2961 case diagnostic_artifact_role::scanned_file
:
2962 case diagnostic_artifact_role::traced_file
:
2963 /* Assume that these are in the source language. */
2964 if (auto client_data_hooks
= m_context
.get_client_data_hooks ())
2965 if (const char *source_lang
2966 = client_data_hooks
->maybe_get_sarif_source_language (filename
))
2967 artifact_obj
->set_string ("sourceLanguage", source_lang
);
2970 case diagnostic_artifact_role::debug_output_file
:
2971 /* Assume that these are not in the source language. */
2975 return *artifact_obj
;
2978 /* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for the
2979 full contents of FILENAME. */
2981 std::unique_ptr
<sarif_artifact_content
>
2982 sarif_builder::maybe_make_artifact_content_object (const char *filename
) const
2984 /* Let input.cc handle any charset conversion. */
2985 char_span utf8_content
2986 = m_context
.get_file_cache ().get_source_file_content (filename
);
2990 /* Don't add it if it's not valid UTF-8. */
2991 if (!cpp_valid_utf8_p(utf8_content
.get_buffer (), utf8_content
.length ()))
2994 auto artifact_content_obj
= ::make_unique
<sarif_artifact_content
> ();
2995 artifact_content_obj
->set
<json::string
>
2997 ::make_unique
<json::string
> (utf8_content
.get_buffer (),
2998 utf8_content
.length ()));
2999 return artifact_content_obj
;
3002 /* Attempt to read the given range of lines from FILENAME; return
3003 a freshly-allocated 0-terminated buffer containing them, or nullptr. */
3006 sarif_builder::get_source_lines (const char *filename
,
3010 auto_vec
<char> result
;
3012 for (int line
= start_line
; line
<= end_line
; line
++)
3014 char_span line_content
3015 = m_context
.get_file_cache ().get_source_line (filename
, line
);
3016 if (!line_content
.get_buffer ())
3018 result
.reserve (line_content
.length () + 1);
3019 for (size_t i
= 0; i
< line_content
.length (); i
++)
3020 result
.quick_push (line_content
[i
]);
3021 result
.quick_push ('\n');
3023 result
.safe_push ('\0');
3025 return xstrdup (result
.address ());
3028 /* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for the given
3029 run of lines within FILENAME (including the endpoints).
3030 If R is non-NULL, use it to potentially set the "rendered"
3031 property (3.3.4). */
3033 std::unique_ptr
<sarif_artifact_content
>
3035 maybe_make_artifact_content_object (const char *filename
,
3038 const content_renderer
*r
) const
3040 char *text_utf8
= get_source_lines (filename
, start_line
, end_line
);
3045 /* Don't add it if it's not valid UTF-8. */
3046 if (!cpp_valid_utf8_p(text_utf8
, strlen(text_utf8
)))
3052 auto artifact_content_obj
= ::make_unique
<sarif_artifact_content
> ();
3053 artifact_content_obj
->set_string ("text", text_utf8
);
3056 /* 3.3.4 "rendered" property. */
3058 if (std::unique_ptr
<sarif_multiformat_message_string
> rendered
3059 = r
->render (*this))
3060 artifact_content_obj
->set ("rendered", std::move (rendered
));
3062 return artifact_content_obj
;
3065 /* Make a "fix" object (SARIF v2.1.0 section 3.55) for RICHLOC. */
3067 std::unique_ptr
<sarif_fix
>
3068 sarif_builder::make_fix_object (const rich_location
&richloc
)
3070 auto fix_obj
= ::make_unique
<sarif_fix
> ();
3072 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
3073 /* We assume that all fix-it hints in RICHLOC affect the same file. */
3074 auto artifact_change_arr
= ::make_unique
<json::array
> ();
3075 artifact_change_arr
->append
<sarif_artifact_change
>
3076 (make_artifact_change_object (richloc
));
3077 fix_obj
->set
<json::array
> ("artifactChanges",
3078 std::move (artifact_change_arr
));
3083 /* Make an "artifactChange" object (SARIF v2.1.0 section 3.56) for RICHLOC. */
3085 std::unique_ptr
<sarif_artifact_change
>
3086 sarif_builder::make_artifact_change_object (const rich_location
&richloc
)
3088 auto artifact_change_obj
= ::make_unique
<sarif_artifact_change
> ();
3090 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
3091 artifact_change_obj
->set
<sarif_artifact_location
>
3092 ("artifactLocation",
3093 make_artifact_location_object (richloc
.get_loc ()));
3095 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
3096 auto replacement_arr
= ::make_unique
<json::array
> ();
3097 for (unsigned int i
= 0; i
< richloc
.get_num_fixit_hints (); i
++)
3099 const fixit_hint
*hint
= richloc
.get_fixit_hint (i
);
3100 replacement_arr
->append
<sarif_replacement
>
3101 (make_replacement_object (*hint
));
3103 artifact_change_obj
->set
<json::array
> ("replacements",
3104 std::move (replacement_arr
));
3106 return artifact_change_obj
;
3109 /* Make a "replacement" object (SARIF v2.1.0 section 3.57) for HINT. */
3111 std::unique_ptr
<sarif_replacement
>
3112 sarif_builder::make_replacement_object (const fixit_hint
&hint
) const
3114 auto replacement_obj
= ::make_unique
<sarif_replacement
> ();
3116 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
3117 replacement_obj
->set
<sarif_region
> ("deletedRegion",
3118 make_region_object_for_hint (hint
));
3120 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
3121 replacement_obj
->set
<sarif_artifact_content
>
3123 make_artifact_content_object (hint
.get_string ()));
3125 return replacement_obj
;
3128 /* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for TEXT. */
3130 std::unique_ptr
<sarif_artifact_content
>
3131 sarif_builder::make_artifact_content_object (const char *text
) const
3133 auto content_obj
= ::make_unique
<sarif_artifact_content
> ();
3135 /* "text" property (SARIF v2.1.0 section 3.3.2). */
3136 content_obj
->set_string ("text", text
);
3141 /* Callback for diagnostic_context::ice_handler_cb for when an ICE
3145 sarif_ice_handler (diagnostic_context
*context
)
3147 /* Attempt to ensure that a .sarif file is written out. */
3148 diagnostic_finish (context
);
3150 /* Print a header for the remaining output to stderr, and
3151 return, attempting to print the usual ICE messages to
3152 stderr. Hopefully this will be helpful to the user in
3153 indicating what's gone wrong (also for DejaGnu, for pruning
3155 fnotice (stderr
, "Internal compiler error:\n");
3158 class sarif_output_format
: public diagnostic_output_format
3161 ~sarif_output_format ()
3163 /* Any sarifResult objects should have been handled by now.
3164 If not, then something's gone wrong with diagnostic
3166 std::unique_ptr
<sarif_result
> pending_result
3167 = m_builder
.take_current_result ();
3168 gcc_assert (!pending_result
);
3171 void on_begin_group () final override
3175 void on_end_group () final override
3177 m_builder
.end_group ();
3180 on_report_diagnostic (const diagnostic_info
&diagnostic
,
3181 diagnostic_t orig_diag_kind
) final override
3183 m_builder
.on_report_diagnostic (diagnostic
, orig_diag_kind
);
3185 void on_diagram (const diagnostic_diagram
&diagram
) final override
3187 m_builder
.emit_diagram (diagram
);
3189 void after_diagnostic (const diagnostic_info
&) final override
3194 sarif_builder
&get_builder () { return m_builder
; }
3197 sarif_output_format (diagnostic_context
&context
,
3198 const line_maps
*line_maps
,
3199 const char *main_input_filename_
,
3201 enum sarif_version version
)
3202 : diagnostic_output_format (context
),
3203 m_builder (context
, line_maps
, main_input_filename_
, formatted
, version
)
3206 sarif_builder m_builder
;
3209 class sarif_stream_output_format
: public sarif_output_format
3212 sarif_stream_output_format (diagnostic_context
&context
,
3213 const line_maps
*line_maps
,
3214 const char *main_input_filename_
,
3216 enum sarif_version version
,
3218 : sarif_output_format (context
, line_maps
, main_input_filename_
,
3219 formatted
, version
),
3223 ~sarif_stream_output_format ()
3225 m_builder
.flush_to_file (m_stream
);
3227 bool machine_readable_stderr_p () const final override
3229 return m_stream
== stderr
;
3235 class sarif_file_output_format
: public sarif_output_format
3238 sarif_file_output_format (diagnostic_context
&context
,
3239 const line_maps
*line_maps
,
3240 const char *main_input_filename_
,
3242 enum sarif_version version
,
3243 diagnostic_output_file output_file
)
3244 : sarif_output_format (context
, line_maps
, main_input_filename_
,
3245 formatted
, version
),
3246 m_output_file (std::move (output_file
))
3248 gcc_assert (m_output_file
.get_open_file ());
3249 gcc_assert (m_output_file
.get_filename ());
3251 ~sarif_file_output_format ()
3253 m_builder
.flush_to_file (m_output_file
.get_open_file ());
3255 bool machine_readable_stderr_p () const final override
3261 diagnostic_output_file m_output_file
;
3264 /* Print the start of an embedded link to PP, as per 3.11.6. */
3267 sarif_begin_embedded_link (pretty_printer
*pp
)
3269 pp_character (pp
, '[');
3272 /* Print the end of an embedded link to PP, as per 3.11.6. */
3275 sarif_end_embedded_link (pretty_printer
*pp
,
3278 pp_string (pp
, "](");
3279 /* TODO: does the URI need escaping?
3280 See https://github.com/oasis-tcs/sarif-spec/issues/657 */
3281 pp_string (pp
, url
);
3282 pp_character (pp
, ')');
3285 /* class sarif_token_printer : public token_printer. */
3287 /* Implementation of pretty_printer::token_printer for SARIF output.
3288 Emit URLs as per 3.11.6 ("Messages with embedded links"). */
3291 sarif_builder::sarif_token_printer::print_tokens (pretty_printer
*pp
,
3292 const pp_token_list
&tokens
)
3294 /* Convert to text, possibly with colorization, URLs, etc. */
3295 label_text current_url
;
3296 for (auto iter
= tokens
.m_first
; iter
; iter
= iter
->m_next
)
3297 switch (iter
->m_kind
)
3302 case pp_token::kind::text
:
3304 const pp_token_text
*sub
= as_a
<const pp_token_text
*> (iter
);
3305 const char * const str
= sub
->m_value
.get ();
3306 if (current_url
.get ())
3308 /* Write iter->m_value, but escaping any
3309 escaped link characters as per 3.11.6. */
3310 for (const char *ptr
= str
; *ptr
; ptr
++)
3312 const char ch
= *ptr
;
3316 pp_character (pp
, ch
);
3321 pp_character (pp
, '\\');
3322 pp_character (pp
, ch
);
3328 /* TODO: is other escaping needed? (e.g. of '[')
3329 See https://github.com/oasis-tcs/sarif-spec/issues/658 */
3330 pp_string (pp
, str
);
3334 case pp_token::kind::begin_color
:
3335 case pp_token::kind::end_color
:
3336 /* These are no-ops. */
3339 case pp_token::kind::begin_quote
:
3340 pp_begin_quote (pp
, pp_show_color (pp
));
3342 case pp_token::kind::end_quote
:
3343 pp_end_quote (pp
, pp_show_color (pp
));
3346 /* Emit URLs as per 3.11.6 ("Messages with embedded links"). */
3347 case pp_token::kind::begin_url
:
3349 pp_token_begin_url
*sub
= as_a
<pp_token_begin_url
*> (iter
);
3350 sarif_begin_embedded_link (pp
);
3351 current_url
= std::move (sub
->m_value
);
3354 case pp_token::kind::end_url
:
3355 gcc_assert (current_url
.get ());
3356 sarif_end_embedded_link (pp
, current_url
.get ());
3357 current_url
= label_text::borrow (nullptr);
3360 case pp_token::kind::event_id
:
3362 pp_token_event_id
*sub
= as_a
<pp_token_event_id
*> (iter
);
3363 gcc_assert (sub
->m_event_id
.known_p ());
3364 const sarif_code_flow
*code_flow
3365 = m_builder
.get_code_flow_for_event_ids ();
3366 label_text url
= make_sarif_url_for_event (code_flow
,
3369 sarif_begin_embedded_link (pp
);
3370 pp_character (pp
, '(');
3371 pp_decimal_int (pp
, sub
->m_event_id
.one_based ());
3372 pp_character (pp
, ')');
3374 sarif_end_embedded_link (pp
, url
.get ());
3380 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
3384 diagnostic_output_format_init_sarif (diagnostic_context
&context
,
3385 std::unique_ptr
<sarif_output_format
> fmt
)
3387 /* Suppress normal textual path output. */
3388 context
.set_path_format (DPF_NONE
);
3390 /* Override callbacks. */
3391 context
.set_ice_handler_callback (sarif_ice_handler
);
3393 /* Don't colorize the text. */
3394 pp_show_color (fmt
->get_printer ()) = false;
3395 context
.set_show_highlight_colors (false);
3397 context
.m_printer
->set_token_printer
3398 (&fmt
->get_builder ().get_token_printer ());
3399 context
.set_output_format (fmt
.release ());
3402 /* Populate CONTEXT in preparation for SARIF output to stderr. */
3405 diagnostic_output_format_init_sarif_stderr (diagnostic_context
&context
,
3406 const line_maps
*line_maps
,
3407 const char *main_input_filename_
,
3409 enum sarif_version version
)
3411 gcc_assert (line_maps
);
3412 diagnostic_output_format_init_sarif
3414 ::make_unique
<sarif_stream_output_format
> (context
,
3416 main_input_filename_
,
3422 /* Populate CONTEXT in preparation for SARIF output to a file named
3423 BASE_FILE_NAME.sarif. */
3426 diagnostic_output_format_init_sarif_file (diagnostic_context
&context
,
3427 line_maps
*line_maps
,
3428 const char *main_input_filename_
,
3430 enum sarif_version version
,
3431 const char *base_file_name
)
3433 gcc_assert (line_maps
);
3435 if (!base_file_name
)
3437 rich_location
richloc (line_maps
, UNKNOWN_LOCATION
);
3438 context
.emit_diagnostic_with_group
3439 (DK_ERROR
, richloc
, nullptr, 0,
3440 "unable to determine filename for SARIF output");
3444 label_text filename
= label_text::take (concat (base_file_name
,
3447 FILE *outf
= fopen (filename
.get (), "w");
3450 rich_location
richloc (line_maps
, UNKNOWN_LOCATION
);
3451 context
.emit_diagnostic_with_group
3452 (DK_ERROR
, richloc
, nullptr, 0,
3453 "unable to open %qs for SARIF output: %m",
3457 diagnostic_output_file
output_file (outf
, true, std::move (filename
));
3458 diagnostic_output_format_init_sarif
3460 ::make_unique
<sarif_file_output_format
> (context
,
3462 main_input_filename_
,
3465 std::move (output_file
)));
3468 /* Populate CONTEXT in preparation for SARIF output to STREAM. */
3471 diagnostic_output_format_init_sarif_stream (diagnostic_context
&context
,
3472 const line_maps
*line_maps
,
3473 const char *main_input_filename_
,
3475 enum sarif_version version
,
3478 gcc_assert (line_maps
);
3479 diagnostic_output_format_init_sarif
3481 ::make_unique
<sarif_stream_output_format
> (context
,
3483 main_input_filename_
,
3491 namespace selftest
{
3493 /* A subclass of sarif_output_format for writing selftests.
3494 The JSON output is cached internally, rather than written
3497 class test_sarif_diagnostic_context
: public test_diagnostic_context
3500 test_sarif_diagnostic_context (const char *main_input_filename
,
3501 enum sarif_version version
)
3503 auto format
= ::make_unique
<buffered_output_format
> (*this,
3505 main_input_filename
,
3508 m_format
= format
.get (); // borrowed
3509 diagnostic_output_format_init_sarif (*this, std::move (format
));
3512 std::unique_ptr
<sarif_log
> flush_to_object ()
3514 return m_format
->flush_to_object ();
3518 class buffered_output_format
: public sarif_output_format
3521 buffered_output_format (diagnostic_context
&context
,
3522 const line_maps
*line_maps
,
3523 const char *main_input_filename_
,
3525 enum sarif_version version
)
3526 : sarif_output_format (context
, line_maps
, main_input_filename_
,
3530 bool machine_readable_stderr_p () const final override
3534 std::unique_ptr
<sarif_log
> flush_to_object ()
3536 return m_builder
.flush_to_object ();
3540 buffered_output_format
*m_format
; // borrowed
3543 /* Test making a sarif_location for a complex rich_location
3544 with labels and escape-on-output. */
3547 test_make_location_object (const line_table_case
&case_
,
3548 enum sarif_version version
)
3550 diagnostic_show_locus_fixture_one_liner_utf8
f (case_
);
3551 location_t line_end
= linemap_position_for_column (line_table
, 31);
3553 /* Don't attempt to run the tests if column data might be unavailable. */
3554 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3557 test_diagnostic_context dc
;
3559 sarif_builder
builder (dc
, line_table
, "MAIN_INPUT_FILENAME", true, version
);
3561 /* These "columns" are byte offsets, whereas later on the columns
3562 in the generated SARIF use sarif_builder::get_sarif_column and
3563 thus respect tabs, encoding. */
3564 const location_t foo
3565 = make_location (linemap_position_for_column (line_table
, 1),
3566 linemap_position_for_column (line_table
, 1),
3567 linemap_position_for_column (line_table
, 8));
3568 const location_t bar
3569 = make_location (linemap_position_for_column (line_table
, 12),
3570 linemap_position_for_column (line_table
, 12),
3571 linemap_position_for_column (line_table
, 17));
3572 const location_t field
3573 = make_location (linemap_position_for_column (line_table
, 19),
3574 linemap_position_for_column (line_table
, 19),
3575 linemap_position_for_column (line_table
, 30));
3577 text_range_label
label0 ("label0");
3578 text_range_label
label1 ("label1");
3579 text_range_label
label2 ("label2");
3581 rich_location
richloc (line_table
, foo
, &label0
, nullptr);
3582 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3583 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3584 richloc
.set_escape_on_output (true);
3586 sarif_result
result (0);
3588 std::unique_ptr
<sarif_location
> location_obj
3589 = builder
.make_location_object
3590 (result
, richloc
, nullptr, diagnostic_artifact_role::analysis_target
);
3591 ASSERT_NE (location_obj
, nullptr);
3593 auto physical_location
3594 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (location_obj
.get (),
3595 "physicalLocation");
3598 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location
, "region");
3599 ASSERT_JSON_INT_PROPERTY_EQ (region
, "startLine", 1);
3600 ASSERT_JSON_INT_PROPERTY_EQ (region
, "startColumn", 1);
3601 ASSERT_JSON_INT_PROPERTY_EQ (region
, "endColumn", 7);
3605 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location
,
3607 ASSERT_JSON_INT_PROPERTY_EQ (context_region
, "startLine", 1);
3611 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (context_region
, "snippet");
3613 /* We expect the snippet's "text" to be a copy of the content. */
3614 ASSERT_JSON_STRING_PROPERTY_EQ (snippet
, "text", f
.m_content
);
3616 /* We expect the snippet to have a "rendered" whose "text" has a
3617 pure ASCII escaped copy of the line (with labels, etc). */
3620 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (snippet
, "rendered");
3621 ASSERT_JSON_STRING_PROPERTY_EQ
3623 "1 | <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
3624 " | ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
3626 " | label0 label1 label2\n");
3631 = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (location_obj
.get (),
3633 ASSERT_EQ (annotations
->size (), 3);
3636 auto a0
= (*annotations
)[0];
3637 ASSERT_JSON_INT_PROPERTY_EQ (a0
, "startLine", 1);
3638 ASSERT_JSON_INT_PROPERTY_EQ (a0
, "startColumn", 1);
3639 ASSERT_JSON_INT_PROPERTY_EQ (a0
, "endColumn", 7);
3641 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (a0
, "message");
3642 ASSERT_JSON_STRING_PROPERTY_EQ (message
, "text", "label0");
3645 auto a1
= (*annotations
)[1];
3646 ASSERT_JSON_INT_PROPERTY_EQ (a1
, "startLine", 1);
3647 ASSERT_JSON_INT_PROPERTY_EQ (a1
, "startColumn", 10);
3648 ASSERT_JSON_INT_PROPERTY_EQ (a1
, "endColumn", 15);
3650 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (a1
, "message");
3651 ASSERT_JSON_STRING_PROPERTY_EQ (message
, "text", "label1");
3654 auto a2
= (*annotations
)[2];
3655 ASSERT_JSON_INT_PROPERTY_EQ (a2
, "startLine", 1);
3656 ASSERT_JSON_INT_PROPERTY_EQ (a2
, "startColumn", 16);
3657 ASSERT_JSON_INT_PROPERTY_EQ (a2
, "endColumn", 25);
3659 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (a2
, "message");
3660 ASSERT_JSON_STRING_PROPERTY_EQ (message
, "text", "label2");
3665 /* Test of reporting a diagnostic at UNKNOWN_LOCATION to a
3666 diagnostic_context and examining the generated sarif_log.
3667 Verify various basic properties. */
3670 test_simple_log (enum sarif_version version
)
3672 test_sarif_diagnostic_context
dc ("MAIN_INPUT_FILENAME", version
);
3674 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
3675 dc
.report (DK_ERROR
, richloc
, nullptr, 0, "this is a test: %i", 42);
3677 auto log_ptr
= dc
.flush_to_object ();
3680 auto log
= log_ptr
.get ();
3681 ASSERT_JSON_STRING_PROPERTY_EQ (log
, "$schema",
3682 sarif_version_to_url (version
));
3683 ASSERT_JSON_STRING_PROPERTY_EQ (log
, "version",
3684 sarif_version_to_property (version
));
3686 auto runs
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log
, "runs"); // 3.13.4
3687 ASSERT_EQ (runs
->size (), 1);
3689 // 3.14 "run" object:
3690 auto run
= (*runs
)[0];
3694 auto tool
= EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (run
, "tool");
3696 EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (tool
, "driver"); // 3.18.2
3702 = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run
, "invocations");
3703 ASSERT_EQ (invocations
->size (), 1);
3706 // 3.20 "invocation" object:
3707 auto invocation
= (*invocations
)[0];
3709 // 3.20.3 arguments property
3711 // 3.20.7 startTimeUtc property
3712 EXPECT_JSON_OBJECT_WITH_STRING_PROPERTY (invocation
, "startTimeUtc");
3714 // 3.20.8 endTimeUtc property
3715 EXPECT_JSON_OBJECT_WITH_STRING_PROPERTY (invocation
, "endTimeUtc");
3717 // 3.20.19 workingDirectory property
3720 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (invocation
,
3721 "workingDirectory");
3722 EXPECT_JSON_OBJECT_WITH_STRING_PROPERTY (wd_obj
, "uri");
3725 // 3.20.21 toolExecutionNotifications property
3727 = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY
3728 (invocation
, "toolExecutionNotifications");
3729 ASSERT_EQ (notifications
->size (), 0);
3735 auto artifacts
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run
, "artifacts");
3736 ASSERT_EQ (artifacts
->size (), 1);
3739 // 3.24 "artifact" object:
3740 auto artifact
= (*artifacts
)[0];
3744 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (artifact
, "location");
3745 ASSERT_JSON_STRING_PROPERTY_EQ (location
, "uri", "MAIN_INPUT_FILENAME");
3748 auto roles
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (artifact
, "roles");
3749 ASSERT_EQ (roles
->size (), 1);
3751 auto role
= (*roles
)[0];
3752 ASSERT_JSON_STRING_EQ (role
, "analysisTarget");
3759 auto results
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run
, "results");
3760 ASSERT_EQ (results
->size (), 1);
3763 // 3.27 "result" object:
3764 auto result
= (*results
)[0];
3765 ASSERT_JSON_STRING_PROPERTY_EQ (result
, "ruleId", "error");
3766 ASSERT_JSON_STRING_PROPERTY_EQ (result
, "level", "error"); // 3.27.10
3771 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result
, "message");
3772 ASSERT_JSON_STRING_PROPERTY_EQ (message
, "text",
3773 "this is a test: 42");
3778 = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (result
, "locations");
3779 ASSERT_EQ (locations
->size (), 0);
3784 /* As above, but with a "real" location_t. */
3787 test_simple_log_2 (const line_table_case
&case_
,
3788 enum sarif_version version
)
3790 auto_fix_quotes fix_quotes
;
3792 const char *const content
3795 = "unsinged int i;\n";
3796 diagnostic_show_locus_fixture
f (case_
, content
);
3797 location_t line_end
= linemap_position_for_column (line_table
, 31);
3799 /* Don't attempt to run the tests if column data might be unavailable. */
3800 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3803 test_sarif_diagnostic_context
dc (f
.get_filename (), version
);
3805 const location_t typo_loc
3806 = make_location (linemap_position_for_column (line_table
, 1),
3807 linemap_position_for_column (line_table
, 1),
3808 linemap_position_for_column (line_table
, 8));
3810 rich_location
richloc (line_table
, typo_loc
);
3811 dc
.report (DK_ERROR
, richloc
, nullptr, 0,
3812 "did you misspell %qs again?",
3815 auto log_ptr
= dc
.flush_to_object ();
3818 auto log
= log_ptr
.get ();
3820 auto runs
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log
, "runs"); // 3.13.4
3821 ASSERT_EQ (runs
->size (), 1);
3823 // 3.14 "run" object:
3824 auto run
= (*runs
)[0];
3828 auto results
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run
, "results");
3829 ASSERT_EQ (results
->size (), 1);
3832 // 3.27 "result" object:
3833 auto result
= (*results
)[0];
3834 ASSERT_JSON_STRING_PROPERTY_EQ (result
, "ruleId", "error");
3835 ASSERT_JSON_STRING_PROPERTY_EQ (result
, "level", "error"); // 3.27.10
3840 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result
, "message");
3841 ASSERT_JSON_STRING_PROPERTY_EQ (message
, "text",
3842 "did you misspell `unsigned' again?");
3847 = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (result
, "locations");
3848 ASSERT_EQ (locations
->size (), 1);
3851 // 3.28 "location" object:
3852 auto location
= (*locations
)[0];
3854 auto physical_location
3855 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (location
,
3856 "physicalLocation");
3859 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location
,
3861 ASSERT_JSON_INT_PROPERTY_EQ (region
, "startLine", 1);
3862 ASSERT_JSON_INT_PROPERTY_EQ (region
, "startColumn", 1);
3863 ASSERT_JSON_INT_PROPERTY_EQ (region
, "endColumn", 9);
3867 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location
,
3869 ASSERT_JSON_INT_PROPERTY_EQ (context_region
, "startLine", 1);
3873 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (context_region
,
3876 /* We expect the snippet's "text" to be a copy of the content. */
3877 ASSERT_JSON_STRING_PROPERTY_EQ (snippet
, "text", f
.m_content
);
3885 /* Assuming that a single diagnostic has been emitted within
3886 LOG, get a json::object for the result object. */
3888 static const json::object
*
3889 get_result_from_log (const sarif_log
*log
)
3891 auto runs
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log
, "runs"); // 3.13.4
3892 ASSERT_EQ (runs
->size (), 1);
3894 // 3.14 "run" object:
3895 auto run
= (*runs
)[0];
3898 auto results
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run
, "results");
3899 ASSERT_EQ (results
->size (), 1);
3901 // 3.27 "result" object:
3902 auto result
= (*results
)[0];
3903 return expect_json_object (SELFTEST_LOCATION
, result
);
3906 /* Assuming that a single diagnostic has been emitted to
3907 DC, get a json::object for the messsage object within
3910 static const json::object
*
3911 get_message_from_log (const sarif_log
*log
)
3913 auto result_obj
= get_result_from_log (log
);
3917 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result_obj
, "message");
3921 /* Tests of messages with embedded links; see SARIF v2.1.0 3.11.6. */
3924 test_message_with_embedded_link (enum sarif_version version
)
3926 auto_fix_quotes fix_quotes
;
3928 test_sarif_diagnostic_context
dc ("test.c", version
);
3929 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
3930 dc
.report (DK_ERROR
, richloc
, nullptr, 0,
3931 "before %{text%} after",
3932 "http://example.com");
3933 std::unique_ptr
<sarif_log
> log
= dc
.flush_to_object ();
3935 auto message_obj
= get_message_from_log (log
.get ());
3936 ASSERT_JSON_STRING_PROPERTY_EQ
3937 (message_obj
, "text",
3938 "before [text](http://example.com) after");
3941 /* Escaping in message text.
3942 This is "EXAMPLE 1" from 3.11.6. */
3944 test_sarif_diagnostic_context
dc ("test.c", version
);
3945 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
3947 /* Disable "unquoted sequence of 2 consecutive punctuation
3948 characters `]\' in format" warning. */
3950 # pragma GCC diagnostic push
3951 # pragma GCC diagnostic ignored "-Wformat-diag"
3953 dc
.report (DK_ERROR
, richloc
, nullptr, 0,
3954 "Prohibited term used in %{para[0]\\spans[2]%}.",
3957 # pragma GCC diagnostic pop
3960 std::unique_ptr
<sarif_log
> log
= dc
.flush_to_object ();
3962 auto message_obj
= get_message_from_log (log
.get ());
3963 ASSERT_JSON_STRING_PROPERTY_EQ
3964 (message_obj
, "text",
3965 "Prohibited term used in [para\\[0\\]\\\\spans\\[2\\]](1).");
3966 /* This isn't exactly what EXAMPLE 1 of the spec has; reported as
3967 https://github.com/oasis-tcs/sarif-spec/issues/656 */
3972 class test_urlifier
: public urlifier
3976 get_url_for_quoted_text (const char *p
, size_t sz
) const final override
3978 if (!strncmp (p
, "-foption", sz
))
3979 return xstrdup ("http://example.com");
3984 test_sarif_diagnostic_context
dc ("test.c", version
);
3985 dc
.set_urlifier (new test_urlifier ());
3986 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
3987 dc
.report (DK_ERROR
, richloc
, nullptr, 0,
3988 "foo %<-foption%> %<unrecognized%> bar");
3989 std::unique_ptr
<sarif_log
> log
= dc
.flush_to_object ();
3991 auto message_obj
= get_message_from_log (log
.get ());
3992 ASSERT_JSON_STRING_PROPERTY_EQ
3993 (message_obj
, "text",
3994 "foo `[-foption](http://example.com)' `unrecognized' bar");
3999 run_tests_per_version (const line_table_case
&case_
)
4001 for (int version_idx
= 0;
4002 version_idx
< (int)sarif_version::num_versions
;
4005 enum sarif_version version
4006 = static_cast<enum sarif_version
> (version_idx
);
4008 test_make_location_object (case_
, version
);
4009 test_simple_log_2 (case_
, version
);
4013 /* Run all of the selftests within this file. */
4016 diagnostic_format_sarif_cc_tests ()
4018 for (int version_idx
= 0;
4019 version_idx
< (int)sarif_version::num_versions
;
4022 enum sarif_version version
4023 = static_cast<enum sarif_version
> (version_idx
);
4025 test_simple_log (version
);
4026 test_message_with_embedded_link (version
);
4029 /* Run tests per (line-table-case, SARIF version) pair. */
4030 for_each_line_table_case (run_tests_per_version
);
4033 } // namespace selftest
4035 #endif /* CHECKING_P */