1 /* SARIF output for diagnostics
2 Copyright (C) 2018-2025 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_STRING
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"
33 #include "diagnostic-buffer.h"
36 #include "logical-location.h"
37 #include "diagnostic-client-data-hooks.h"
38 #include "diagnostic-diagram.h"
39 #include "text-art/canvas.h"
40 #include "diagnostic-format-sarif.h"
41 #include "diagnostic-format-text.h"
42 #include "ordered-hash-map.h"
44 #include "make-unique.h"
46 #include "selftest-diagnostic.h"
47 #include "selftest-diagnostic-show-locus.h"
48 #include "selftest-json.h"
49 #include "text-range-label.h"
50 #include "pretty-print-format-impl.h"
51 #include "pretty-print-urlifier.h"
53 #include "backtrace.h"
57 class content_renderer
;
58 class escape_nonascii_renderer
;
60 /* Subclasses of sarif_object.
61 Keep these in order of their descriptions in the specification. */
62 class sarif_artifact_content
; // 3.3
63 class sarif_artifact_location
; // 3.4
64 class sarif_message
; // 3.11
65 class sarif_multiformat_message_string
; // 3.12
66 class sarif_log
; // 3.13
67 class sarif_run
; // 3.14
68 class sarif_tool
; // 3.18
69 class sarif_tool_component
; // 3.19
70 class sarif_invocation
; // 3.20
71 class sarif_artifact
; // 3.24
72 class sarif_location_manager
; // not in the spec
73 class sarif_result
; // 3.27
74 class sarif_location
; // 3.28
75 class sarif_physical_location
; // 3.29
76 class sarif_region
; // 3.30
77 class sarif_logical_location
; // 3.33
78 class sarif_location_relationship
; // 3.34
79 class sarif_code_flow
; // 3.36
80 class sarif_thread_flow
; // 3.37
81 class sarif_thread_flow_location
; // 3.38
82 class sarif_reporting_descriptor
; // 3.49
83 class sarif_reporting_descriptor_reference
; // 3.53
84 class sarif_tool_component_reference
; // 3.54
85 class sarif_fix
; // 3.55
86 class sarif_artifact_change
; // 3.56
87 class sarif_replacement
; // 3.57
88 class sarif_ice_notification
; // 3.58
90 // Valid values for locationRelationship's "kinds" property (3.34.3)
92 enum class location_relationship_kind
101 /* Declarations of subclasses of sarif_object.
102 Keep these in order of their descriptions in the specification. */
104 /* Subclass of sarif_object for SARIF "artifactContent" objects
105 (SARIF v2.1.0 section 3.3). */
107 class sarif_artifact_content
: public sarif_object
{};
109 /* Subclass of sarif_object for SARIF "artifactLocation" objects
110 (SARIF v2.1.0 section 3.4). */
112 class sarif_artifact_location
: public sarif_object
{};
114 /* Subclass of sarif_object for SARIF "message" objects
115 (SARIF v2.1.0 section 3.11). */
117 class sarif_message
: public sarif_object
{};
119 /* Subclass of sarif_object for SARIF "multiformatMessageString" objects
120 (SARIF v2.1.0 section 3.12). */
122 class sarif_multiformat_message_string
: public sarif_object
{};
124 /* Subclass of sarif_object for SARIF "log" objects
125 (SARIF v2.1.0 section 3.13). */
127 class sarif_log
: public sarif_object
{};
129 /* Subclass of sarif_object for SARIF "run" objects
130 (SARIF v2.1.0 section 3.14). */
132 class sarif_run
: public sarif_object
{};
134 /* Subclass of sarif_object for SARIF "tool" objects
135 (SARIF v2.1.0 section 3.18). */
137 class sarif_tool
: public sarif_object
{};
139 /* Subclass of sarif_object for SARIF "toolComponent" objects
140 (SARIF v2.1.0 section 3.19). */
142 class sarif_tool_component
: public sarif_object
{};
144 /* Make a JSON string for the current date and time.
145 See SARIF v2.1.0 section 3.9 "Date/time properties".
146 Given that we don't run at the very beginning/end of the
147 process, it doesn't make sense to be more accurate than
148 the current second. */
150 static std::unique_ptr
<json::string
>
151 make_date_time_string_for_current_time ()
153 time_t t
= time (nullptr);
154 struct tm
*tm
= gmtime (&t
);
156 snprintf (buf
, sizeof (buf
) - 1,
159 tm
->tm_year
+ 1900, tm
->tm_mon
+ 1, tm
->tm_mday
,
160 tm
->tm_hour
, tm
->tm_min
, tm
->tm_sec
);
161 return ::make_unique
<json::string
> (buf
);
164 /* Subclass of sarif_object for SARIF "invocation" objects
165 (SARIF v2.1.0 section 3.20). */
167 class sarif_invocation
: public sarif_object
170 sarif_invocation (sarif_builder
&builder
,
171 const char * const *original_argv
);
173 void add_notification_for_ice (const diagnostic_info
&diagnostic
,
174 sarif_builder
&builder
,
175 std::unique_ptr
<json::object
> backtrace
);
176 void prepare_to_flush (sarif_builder
&builder
);
179 std::unique_ptr
<json::array
> m_notifications_arr
;
183 /* Corresponds to values for the SARIF artifact objects "roles" property.
184 (SARIF v2.1.0 section 3.24.6). */
186 enum class diagnostic_artifact_role
188 analysis_target
, /* "analysisTarget". */
189 debug_output_file
, /* "debugOutputFile". */
190 result_file
, /* "resultFile". */
192 /* "scannedFile" added in 2.2;
193 see https://github.com/oasis-tcs/sarif-spec/issues/459 */
196 traced_file
, /* "tracedFile". */
201 /* Subclass of sarif_object for SARIF artifact objects
202 (SARIF v2.1.0 section 3.24). */
204 class sarif_artifact
: public sarif_object
207 sarif_artifact (const char *filename
)
208 : m_filename (filename
),
209 m_roles ((unsigned)diagnostic_artifact_role::NUM_ROLES
),
210 m_embed_contents (false)
212 bitmap_clear (m_roles
);
215 void add_role (enum diagnostic_artifact_role role
,
216 bool embed_contents
);
218 bool embed_contents_p () const { return m_embed_contents
; }
219 void populate_contents (sarif_builder
&builder
);
220 void populate_roles ();
223 const char *m_filename
;
224 auto_sbitmap m_roles
;
226 /* Flag to track whether this artifact should have a "contents" property
227 (SARIF v2.1.0 section 3.24.8).
228 We only add the contents for those artifacts that have a location
229 referencing them (so that a consumer might want to quote the source). */
230 bool m_embed_contents
;
233 /* A class for sarif_objects that own a "namespace" of numeric IDs for
234 managing location objects within them. Currently (SARIF v2.1.0)
235 this is just for sarif_result (section 3.28.2), but it will likely
236 eventually also be for notification objects; see
237 https://github.com/oasis-tcs/sarif-spec/issues/540
239 Consider locations with chains of include information e.g.
242 > #include "include-chain-1.h"
245 | // First set of decls, which will be referenced in notes
246 | #include "include-chain-1-1.h"
248 | // Second set of decls, which will trigger the errors
249 | #include "include-chain-1-2.h"
259 GCC's textual output emits:
260 | In file included from PATH/include-chain-1.h:5,
261 | from PATH/include-chain-1.c:30:
262 | PATH/include-chain-1-2.h:1:6: error: conflicting types for 'p'; have 'char'
265 | In file included from PATH/include-chain-1.h:2:
266 | PATH/include-chain-1-1.h:1:5: note: previous declaration of 'p' with type 'int'
269 | PATH/include-chain-1-2.h:2:6: error: conflicting types for 'q'; have 'char'
272 | PATH/include-chain-1-1.h:2:5: note: previous declaration of 'q' with type 'int'
276 Whenever a SARIF location is added for a location_t that
277 was #included from somewhere, we queue up the creation of a SARIF
278 location for the location of the #include. The worklist of queued
279 locations is flushed when the result is finished, which lazily creates
280 any additional related locations for the include chain, and the
281 relationships between the locations. Doing so can lead to further
282 include locations being processed. The worklist approach allows us
283 to lazily explore the relevant part of the directed graph of location_t
284 values implicit in our line_maps structure, replicating it as a directed
285 graph of SARIF locations within the SARIF result object, like this:
287 [0]: error in include-chain-1-2.h ("conflicting types for 'p'; have 'char'")
288 [1]: #include "include-chain-1-2.h" in include-chain-1.h
289 [2]: note in include-chain-1-2.h ("previous declaration of 'p' with type 'int'")
290 [3]: #include "include-chain-1-1.h" in include-chain-1.h
291 [4]: #include "include-chain-1.h" in include-chain-1.c
293 where we want to capture this "includes" graph in SARIF form:
294 . +-----------------------------------+ +----------------------------------+
295 . |"id": 0 | |"id": 2 |
296 . | error: "conflicting types for 'p';| | note: previous declaration of 'p'|
297 . | have 'char'"| | | with type 'int'") |
298 . | in include-chain-1-2.h | | in include-chain-1-1.h |
299 . +-----------------------------------+ +----------------------------------+
301 . includes | | included-by includes | | included-by
303 . +--------------------------------+ +--------------------------------+
304 . |"id": 1 | |"id": 3 |
305 . | #include "include-chain-1-2.h" | | #include "include-chain-1-1.h" |
306 . | in include-chain-1.h | | in include-chain-1.h |
307 . +--------------------------------+ +--------------------------------+
309 . includes | | included-by includes | | included-by
311 . +------------------------------------+
313 . | The #include "include-chain-1.h" |
314 . | in include-chain-1.c |
315 . +------------------------------------+
318 class sarif_location_manager
: public sarif_object
321 /* A worklist of pending actions needed to fully process this object.
323 This lets us lazily walk our data structures to build the
324 directed graph of locations, whilst keeping "notes" at the top
325 of the "relatedLocations" array, and avoiding the need for
331 /* Process a #include relationship where m_location_obj
332 was #included-d at m_where. */
335 /* Process a location_t that was added as a secondary location
336 to a rich_location without a label. */
337 unlabelled_secondary_location
340 worklist_item (sarif_location
&location_obj
,
343 : m_location_obj (location_obj
),
349 sarif_location
&m_location_obj
;
354 sarif_location_manager ()
355 : m_related_locations_arr (nullptr),
356 m_next_location_id (0)
360 unsigned allocate_location_id ()
362 return m_next_location_id
++;
366 add_related_location (std::unique_ptr
<sarif_location
> location_obj
,
367 sarif_builder
&builder
);
370 add_relationship_to_worklist (sarif_location
&location_obj
,
371 enum worklist_item::kind kind
,
375 process_worklist (sarif_builder
&builder
);
378 process_worklist_item (sarif_builder
&builder
,
379 const worklist_item
&item
);
381 json::array
*m_related_locations_arr
; // borrowed
382 unsigned m_next_location_id
;
384 std::list
<worklist_item
> m_worklist
;
385 std::map
<location_t
, sarif_location
*> m_included_from_locations
;
386 std::map
<location_t
, sarif_location
*> m_unlabelled_secondary_locations
;
389 /* Subclass of sarif_object for SARIF "result" objects
390 (SARIF v2.1.0 section 3.27).
391 Each SARIF result object has its own "namespace" of numeric IDs for
392 managing location objects (SARIF v2.1.0 section 3.28.2). */
394 class sarif_result
: public sarif_location_manager
397 sarif_result (unsigned idx_within_parent
)
398 : m_idx_within_parent (idx_within_parent
)
401 unsigned get_index_within_parent () const { return m_idx_within_parent
; }
404 on_nested_diagnostic (const diagnostic_info
&diagnostic
,
405 diagnostic_t orig_diag_kind
,
406 sarif_builder
&builder
);
407 void on_diagram (const diagnostic_diagram
&diagram
,
408 sarif_builder
&builder
);
411 const unsigned m_idx_within_parent
;
414 /* Subclass of sarif_object for SARIF "location" objects
415 (SARIF v2.1.0 section 3.28).
416 A location object can have an "id" which must be unique within
417 the enclosing result, if any (see SARIF v2.1.0 section 3.28.2). */
419 class sarif_location
: public sarif_object
422 long lazily_add_id (sarif_location_manager
&loc_mgr
);
423 long get_id () const;
425 void lazily_add_relationship (sarif_location
&target
,
426 enum location_relationship_kind kind
,
427 sarif_location_manager
&loc_mgr
);
430 sarif_location_relationship
&
431 lazily_add_relationship_object (sarif_location
&target
,
432 sarif_location_manager
&loc_mgr
);
434 json::array
&lazily_add_relationships_array ();
436 std::map
<sarif_location
*,
437 sarif_location_relationship
*> m_relationships_map
;
440 /* Subclass of sarif_object for SARIF "physicalLocation" objects
441 (SARIF v2.1.0 section 3.29). */
443 class sarif_physical_location
: public sarif_object
{};
445 /* Subclass of sarif_object for SARIF "region" objects
446 (SARIF v2.1.0 section 3.30). */
448 class sarif_region
: public sarif_object
{};
450 /* Subclass of sarif_object for SARIF "locationRelationship" objects
451 (SARIF v2.1.0 section 3.34). */
453 class sarif_location_relationship
: public sarif_object
456 sarif_location_relationship (sarif_location
&target
,
457 sarif_location_manager
&loc_mgr
);
459 long get_target_id () const;
461 void lazily_add_kind (enum location_relationship_kind kind
);
464 auto_sbitmap m_kinds
;
467 /* Subclass of sarif_object for SARIF "codeFlow" objects
468 (SARIF v2.1.0 section 3.36). */
470 class sarif_code_flow
: public sarif_object
473 sarif_code_flow (sarif_result
&parent
,
474 unsigned idx_within_parent
);
476 sarif_result
&get_parent () const { return m_parent
; }
477 unsigned get_index_within_parent () const { return m_idx_within_parent
; }
480 get_or_append_thread_flow (const diagnostic_thread
&thread
,
481 diagnostic_thread_id_t thread_id
);
484 get_thread_flow (diagnostic_thread_id_t thread_id
);
486 void add_location (sarif_thread_flow_location
&);
488 sarif_thread_flow_location
&
489 get_thread_flow_loc_obj (diagnostic_event_id_t event_id
) const;
492 sarif_result
&m_parent
;
493 const unsigned m_idx_within_parent
;
495 hash_map
<int_hash
<diagnostic_thread_id_t
, -1, -2>,
496 sarif_thread_flow
*> m_thread_id_map
; // borrowed ptr
497 json::array
*m_thread_flows_arr
; // borrowed
499 /* Vec of borrowed ptr, allowing for going easily from
500 an event_id to the corresponding threadFlowLocation object. */
501 std::vector
<sarif_thread_flow_location
*> m_all_tfl_objs
;
504 /* Subclass of sarif_object for SARIF "threadFlow" objects
505 (SARIF v2.1.0 section 3.37). */
507 class sarif_thread_flow
: public sarif_object
510 sarif_thread_flow (sarif_code_flow
&parent
,
511 const diagnostic_thread
&thread
,
512 unsigned idx_within_parent
);
514 sarif_code_flow
&get_parent () const { return m_parent
; }
515 unsigned get_index_within_parent () const { return m_idx_within_parent
; }
517 sarif_thread_flow_location
&add_location ();
520 sarif_code_flow
&m_parent
;
521 json::array
*m_locations_arr
; // borrowed
522 const unsigned m_idx_within_parent
;
525 /* Subclass of sarif_object for SARIF "threadFlowLocation" objects
526 (SARIF v2.1.0 section 3.38). */
528 class sarif_thread_flow_location
: public sarif_object
531 sarif_thread_flow_location (sarif_thread_flow
&parent
,
532 unsigned idx_within_parent
)
534 m_idx_within_parent (idx_within_parent
)
538 sarif_thread_flow
&get_parent () const { return m_parent
; }
539 unsigned get_index_within_parent () const { return m_idx_within_parent
; }
542 sarif_thread_flow
&m_parent
;
543 const unsigned m_idx_within_parent
;
546 /* Subclass of sarif_object for SARIF "reportingDescriptor" objects
547 (SARIF v2.1.0 section 3.49). */
549 class sarif_reporting_descriptor
: public sarif_object
{};
551 /* Subclass of sarif_object for SARIF "reportingDescriptorReference" objects
552 (SARIF v2.1.0 section 3.53). */
554 class sarif_reporting_descriptor_reference
: public sarif_object
{};
556 /* Subclass of sarif_object for SARIF "toolComponentReference" objects
557 (SARIF v2.1.0 section 3.54). */
559 class sarif_tool_component_reference
: public sarif_object
{};
561 /* Subclass of sarif_object for SARIF "fix" objects
562 (SARIF v2.1.0 section 3.55). */
564 class sarif_fix
: public sarif_object
{};
566 /* Subclass of sarif_object for SARIF "artifactChange" objects
567 (SARIF v2.1.0 section 3.56). */
569 class sarif_artifact_change
: public sarif_object
{};
571 /* Subclass of sarif_object for SARIF "replacement" objects
572 (SARIF v2.1.0 section 3.57). */
574 class sarif_replacement
: public sarif_object
{};
576 /* Subclass of sarif_object for SARIF "notification" objects
577 (SARIF v2.1.0 section 3.58).
579 This subclass is specifically for notifying when an
580 internal compiler error occurs. */
582 class sarif_ice_notification
: public sarif_location_manager
585 sarif_ice_notification (const diagnostic_info
&diagnostic
,
586 sarif_builder
&builder
,
587 std::unique_ptr
<json::object
> backtrace
);
590 add_related_location (std::unique_ptr
<sarif_location
> location_obj
,
591 sarif_builder
&builder
) final override
;
594 /* Abstract base class for use when making an "artifactContent"
595 object (SARIF v2.1.0 section 3.3): generate a value for the
596 3.3.4 "rendered" property.
597 Can return nullptr, for "no property". */
599 class content_renderer
602 virtual ~content_renderer () {}
604 virtual std::unique_ptr
<sarif_multiformat_message_string
>
605 render (const sarif_builder
&builder
) const = 0;
608 /* Concrete buffering implementation subclass for JSON output. */
610 class diagnostic_sarif_format_buffer
: public diagnostic_per_format_buffer
613 friend class sarif_output_format
;
615 diagnostic_sarif_format_buffer (sarif_builder
&builder
)
616 : m_builder (builder
)
619 void dump (FILE *out
, int indent
) const final override
;
620 bool empty_p () const final override
;
621 void move_to (diagnostic_per_format_buffer
&dest
) final override
;
622 void clear () final override
;
623 void flush () final override
;
625 void add_result (std::unique_ptr
<sarif_result
> result
)
627 m_results
.push_back (std::move (result
));
630 size_t num_results () const { return m_results
.size (); }
631 sarif_result
&get_result (size_t idx
) { return *m_results
[idx
]; }
634 sarif_builder
&m_builder
;
635 std::vector
<std::unique_ptr
<sarif_result
>> m_results
;
638 /* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
639 and -fdiagnostics-format=sarif-file).
641 As diagnostics occur, we build "result" JSON objects, and
643 - which source files are referenced
644 - which warnings are emitted
645 - which CWEs are used
647 At the end of the compile, we use the above to build the full SARIF
648 object tree, adding the result objects to the correct place, and
649 creating objects for the various source files, warnings and CWEs
655 - diagnostic groups (see limitations below)
656 - logical locations (e.g. cfun)
657 - labelled ranges (as annotations)
658 - secondary ranges without labels (as related locations)
661 - GCC supports nesting of diagnostics (one-deep nesting via
662 auto_diagnostic_group, and arbitrary nesting via
663 auto_diagnostic_nesting_level). These are captured in the SARIF
664 as related locations, and so we only capture location and message
665 information from such nested diagnostics (e.g. we ignore fix-it
666 hints on them). Diagnostics within an auto_diagnostic_nesting_level
667 have their nesting level captured as a property.
668 - although we capture command-line arguments (section 3.20.2), we don't
669 yet capture response files.
670 - doesn't capture "artifact.encoding" property
671 (SARIF v2.1.0 section 3.24.9).
672 - doesn't capture hashes of the source files
673 ("artifact.hashes" property (SARIF v2.1.0 section 3.24.11).
674 - doesn't capture the "analysisTarget" property
675 (SARIF v2.1.0 section 3.27.13).
676 - doesn't capture -Werror cleanly
677 - doesn't capture inlining information (can SARIF handle this?)
678 - doesn't capture macro expansion information (can SARIF handle this?).
679 - doesn't capture any diagnostic_metadata::rules associated with
685 friend class diagnostic_sarif_format_buffer
;
687 sarif_builder (diagnostic_context
&context
,
688 pretty_printer
&printer
,
689 const line_maps
*line_maps
,
690 const char *main_input_filename_
,
692 enum sarif_version version
);
695 void set_printer (pretty_printer
&printer
)
697 m_printer
= &printer
;
700 void on_report_diagnostic (const diagnostic_info
&diagnostic
,
701 diagnostic_t orig_diag_kind
,
702 diagnostic_sarif_format_buffer
*buffer
);
703 void emit_diagram (const diagnostic_diagram
&diagram
);
706 std::unique_ptr
<sarif_result
> take_current_result ()
708 return std::move (m_cur_group_result
);
711 std::unique_ptr
<sarif_log
> flush_to_object ();
712 void flush_to_file (FILE *outf
);
714 std::unique_ptr
<json::array
>
715 make_locations_arr (sarif_location_manager
&loc_mgr
,
716 const diagnostic_info
&diagnostic
,
717 enum diagnostic_artifact_role role
);
718 std::unique_ptr
<sarif_location
>
719 make_location_object (sarif_location_manager
&loc_mgr
,
720 const rich_location
&rich_loc
,
721 const logical_location
*logical_loc
,
722 enum diagnostic_artifact_role role
);
723 std::unique_ptr
<sarif_location
>
724 make_location_object (sarif_location_manager
&loc_mgr
,
726 enum diagnostic_artifact_role role
);
727 std::unique_ptr
<sarif_message
>
728 make_message_object (const char *msg
) const;
729 std::unique_ptr
<sarif_message
>
730 make_message_object_for_diagram (const diagnostic_diagram
&diagram
);
731 std::unique_ptr
<sarif_artifact_content
>
732 maybe_make_artifact_content_object (const char *filename
) const;
734 std::unique_ptr
<sarif_artifact_location
>
735 make_artifact_location_object (const char *filename
);
737 const sarif_code_flow
*
738 get_code_flow_for_event_ids () const
740 return m_current_code_flow
;
743 diagnostic_context
&get_context () const { return m_context
; }
744 pretty_printer
*get_printer () const { return m_printer
; }
745 token_printer
&get_token_printer () { return m_token_printer
; }
746 enum sarif_version
get_version () const { return m_version
; }
748 size_t num_results () const { return m_results_array
->size (); }
749 sarif_result
&get_result (size_t idx
)
751 auto element
= (*m_results_array
)[idx
];
752 gcc_assert (element
);
753 return *static_cast<sarif_result
*> (element
);
757 class sarif_token_printer
: public token_printer
760 sarif_token_printer (sarif_builder
&builder
)
761 : m_builder (builder
)
764 void print_tokens (pretty_printer
*pp
,
765 const pp_token_list
&tokens
) final override
;
767 sarif_builder
&m_builder
;
770 std::unique_ptr
<sarif_result
>
771 make_result_object (const diagnostic_info
&diagnostic
,
772 diagnostic_t orig_diag_kind
,
773 unsigned idx_within_parent
);
775 add_any_include_chain (sarif_location_manager
&loc_mgr
,
776 sarif_location
&location_obj
,
779 set_any_logical_locs_arr (sarif_location
&location_obj
,
780 const logical_location
*logical_loc
);
781 std::unique_ptr
<sarif_location
>
782 make_location_object (sarif_location_manager
&loc_mgr
,
783 const diagnostic_event
&event
,
784 enum diagnostic_artifact_role role
);
785 std::unique_ptr
<sarif_code_flow
>
786 make_code_flow_object (sarif_result
&result
,
787 unsigned idx_within_parent
,
788 const diagnostic_path
&path
);
790 populate_thread_flow_location_object (sarif_result
&result
,
791 sarif_thread_flow_location
&thread_flow_loc_obj
,
792 const diagnostic_event
&event
,
793 int event_execution_idx
);
794 std::unique_ptr
<json::array
>
795 maybe_make_kinds_array (diagnostic_event::meaning m
) const;
796 std::unique_ptr
<sarif_physical_location
>
797 maybe_make_physical_location_object (location_t loc
,
798 enum diagnostic_artifact_role role
,
800 const content_renderer
*snippet_renderer
);
801 std::unique_ptr
<sarif_artifact_location
>
802 make_artifact_location_object (location_t loc
);
803 std::unique_ptr
<sarif_artifact_location
>
804 make_artifact_location_object_for_pwd () const;
805 std::unique_ptr
<sarif_region
>
806 maybe_make_region_object (location_t loc
,
807 int column_override
) const;
808 std::unique_ptr
<sarif_region
>
809 maybe_make_region_object_for_context (location_t loc
,
810 const content_renderer
*snippet_renderer
) const;
811 std::unique_ptr
<sarif_region
>
812 make_region_object_for_hint (const fixit_hint
&hint
) const;
813 std::unique_ptr
<sarif_multiformat_message_string
>
814 make_multiformat_message_string (const char *msg
) const;
815 std::unique_ptr
<sarif_log
>
816 make_top_level_object (std::unique_ptr
<sarif_invocation
> invocation_obj
,
817 std::unique_ptr
<json::array
> results
);
818 std::unique_ptr
<sarif_run
>
819 make_run_object (std::unique_ptr
<sarif_invocation
> invocation_obj
,
820 std::unique_ptr
<json::array
> results
);
821 std::unique_ptr
<sarif_tool
>
823 std::unique_ptr
<sarif_tool_component
>
824 make_driver_tool_component_object ();
825 std::unique_ptr
<json::array
> maybe_make_taxonomies_array () const;
826 std::unique_ptr
<sarif_tool_component
>
827 maybe_make_cwe_taxonomy_object () const;
828 std::unique_ptr
<sarif_tool_component_reference
>
829 make_tool_component_reference_object_for_cwe () const;
830 std::unique_ptr
<sarif_reporting_descriptor
>
831 make_reporting_descriptor_object_for_warning (const diagnostic_info
&diagnostic
,
832 diagnostic_t orig_diag_kind
,
833 const char *option_text
);
834 std::unique_ptr
<sarif_reporting_descriptor
>
835 make_reporting_descriptor_object_for_cwe_id (int cwe_id
) const;
836 std::unique_ptr
<sarif_reporting_descriptor_reference
>
837 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id
);
839 get_or_create_artifact (const char *filename
,
840 enum diagnostic_artifact_role role
,
841 bool embed_contents
);
843 get_source_lines (const char *filename
,
846 std::unique_ptr
<sarif_artifact_content
>
847 maybe_make_artifact_content_object (const char *filename
,
850 const content_renderer
*r
) const;
851 std::unique_ptr
<sarif_fix
>
852 make_fix_object (const rich_location
&rich_loc
);
853 std::unique_ptr
<sarif_artifact_change
>
854 make_artifact_change_object (const rich_location
&richloc
);
855 std::unique_ptr
<sarif_replacement
>
856 make_replacement_object (const fixit_hint
&hint
) const;
857 std::unique_ptr
<sarif_artifact_content
>
858 make_artifact_content_object (const char *text
) const;
859 int get_sarif_column (expanded_location exploc
) const;
861 std::unique_ptr
<json::object
>
862 make_stack_from_backtrace ();
864 diagnostic_context
&m_context
;
865 pretty_printer
*m_printer
;
866 const line_maps
*m_line_maps
;
867 sarif_token_printer m_token_printer
;
868 enum sarif_version m_version
;
870 /* The JSON object for the invocation object. */
871 std::unique_ptr
<sarif_invocation
> m_invocation_obj
;
873 /* The JSON array of pending diagnostics. */
874 std::unique_ptr
<json::array
> m_results_array
;
876 /* The JSON object for the result object (if any) in the current
878 std::unique_ptr
<sarif_result
> m_cur_group_result
;
880 /* Ideally we'd use std::unique_ptr<sarif_artifact> here, but I had
881 trouble getting this to work when building with GCC 4.8. */
882 ordered_hash_map
<nofree_string_hash
,
883 sarif_artifact
*> m_filename_to_artifact_map
;
885 bool m_seen_any_relative_paths
;
886 hash_set
<free_string_hash
> m_rule_id_set
;
887 std::unique_ptr
<json::array
> m_rules_arr
;
889 /* The set of all CWE IDs we've seen, if any. */
890 hash_set
<int_hash
<int, 0, 1> > m_cwe_id_set
;
896 unsigned m_next_result_idx
;
897 sarif_code_flow
*m_current_code_flow
;
900 /* class sarif_object : public json::object. */
903 sarif_object::get_or_create_properties ()
905 json::value
*properties_val
= get ("properties");
908 if (properties_val
->get_kind () == json::JSON_OBJECT
)
909 return *static_cast <sarif_property_bag
*> (properties_val
);
912 sarif_property_bag
*bag
= new sarif_property_bag ();
913 set ("properties", bag
);
917 /* class sarif_invocation : public sarif_object. */
919 sarif_invocation::sarif_invocation (sarif_builder
&builder
,
920 const char * const *original_argv
)
921 : m_notifications_arr (::make_unique
<json::array
> ()),
924 // "arguments" property (SARIF v2.1.0 section 3.20.2)
927 auto arguments_arr
= ::make_unique
<json::array
> ();
928 for (size_t i
= 0; original_argv
[i
]; ++i
)
929 arguments_arr
->append_string (original_argv
[i
]);
930 set
<json::array
> ("arguments", std::move (arguments_arr
));
933 // "workingDirectory" property (SARIF v2.1.0 section 3.20.19)
934 if (const char *pwd
= getpwd ())
935 set
<sarif_artifact_location
> ("workingDirectory",
936 builder
.make_artifact_location_object (pwd
));
938 // "startTimeUtc" property (SARIF v2.1.0 section 3.20.7)
939 set
<json::string
> ("startTimeUtc",
940 make_date_time_string_for_current_time ());
943 /* Handle an internal compiler error DIAGNOSTIC.
944 Add an object representing the ICE to the notifications array. */
947 sarif_invocation::add_notification_for_ice (const diagnostic_info
&diagnostic
,
948 sarif_builder
&builder
,
949 std::unique_ptr
<json::object
> backtrace
)
954 = ::make_unique
<sarif_ice_notification
> (diagnostic
,
956 std::move (backtrace
));
958 /* Support for related locations within a notification was added
959 in SARIF 2.2; see https://github.com/oasis-tcs/sarif-spec/issues/540 */
960 if (builder
.get_version () >= sarif_version::v2_2_prerelease_2024_08_08
)
961 notification
->process_worklist (builder
);
963 m_notifications_arr
->append
<sarif_ice_notification
>
964 (std::move (notification
));
968 sarif_invocation::prepare_to_flush (sarif_builder
&builder
)
970 const diagnostic_context
&context
= builder
.get_context ();
972 /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
973 if (context
.execution_failed_p ())
975 set_bool ("executionSuccessful", m_success
);
977 /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
978 set ("toolExecutionNotifications", std::move (m_notifications_arr
));
980 /* Call client hook, allowing it to create a custom property bag for
981 this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */
982 if (auto client_data_hooks
= context
.get_client_data_hooks ())
983 client_data_hooks
->add_sarif_invocation_properties (*this);
985 // "endTimeUtc" property (SARIF v2.1.0 section 3.20.8);
986 set
<json::string
> ("endTimeUtc",
987 make_date_time_string_for_current_time ());
990 /* class sarif_artifact : public sarif_object. */
992 /* Add ROLE to this artifact's roles.
993 If EMBED_CONTENTS is true, then flag that we will attempt to embed the
994 contents of this artifact when writing it out. */
997 sarif_artifact::add_role (enum diagnostic_artifact_role role
,
1000 /* TODO(SARIF 2.2): "scannedFile" is to be added as a role in SARIF 2.2;
1001 see https://github.com/oasis-tcs/sarif-spec/issues/459
1004 Ultimately, we probably shouldn't bother embedding the contents
1005 of such artifacts, just the snippets. */
1006 if (role
== diagnostic_artifact_role::scanned_file
)
1010 m_embed_contents
= true;
1012 /* In SARIF v2.1.0 section 3.24.6 "roles" property:
1013 "resultFile" is for an artifact
1014 "which the analysis tool was not explicitly instructed to scan",
1015 whereas "analysisTarget" is for one where the
1016 "analysis tool was instructed to scan this artifact".
1017 Hence the latter excludes the former. */
1018 if (role
== diagnostic_artifact_role::result_file
)
1019 if (bitmap_bit_p (m_roles
, (int)diagnostic_artifact_role::analysis_target
))
1022 bitmap_set_bit (m_roles
, (int)role
);
1025 /* Populate the "contents" property (SARIF v2.1.0 section 3.24.8).
1026 We do this after initialization to
1027 (a) ensure that any charset options have been set
1028 (b) only populate it for artifacts that are referenced by a location. */
1031 sarif_artifact::populate_contents (sarif_builder
&builder
)
1033 if (auto artifact_content_obj
1034 = builder
.maybe_make_artifact_content_object (m_filename
))
1035 set
<sarif_artifact_content
> ("contents", std::move (artifact_content_obj
));
1038 /* Get a string for ROLE corresponding to the
1039 SARIF v2.1.0 section 3.24.6 "roles" property. */
1042 get_artifact_role_string (enum diagnostic_artifact_role role
)
1048 case diagnostic_artifact_role::analysis_target
:
1049 return "analysisTarget";
1050 case diagnostic_artifact_role::debug_output_file
:
1051 return "debugOutputFile";
1052 case diagnostic_artifact_role::result_file
:
1053 return "resultFile";
1054 case diagnostic_artifact_role::scanned_file
:
1055 return "scannedFile";
1056 case diagnostic_artifact_role::traced_file
:
1057 return "tracedFile";
1061 /* Populate the "roles" property of this sarif_artifact with a new
1062 json::array for the artifact.roles property (SARIF v2.1.0 section 3.24.6)
1063 containing strings such as "analysisTarget", "resultFile"
1064 and/or "tracedFile". */
1067 sarif_artifact::populate_roles ()
1069 if (bitmap_empty_p (m_roles
))
1071 auto roles_arr (::make_unique
<json::array
> ());
1072 for (int i
= 0; i
< (int)diagnostic_artifact_role::NUM_ROLES
; i
++)
1073 if (bitmap_bit_p (m_roles
, i
))
1075 enum diagnostic_artifact_role role
= (enum diagnostic_artifact_role
)i
;
1076 roles_arr
->append_string (get_artifact_role_string (role
));
1078 set
<json::array
> ("roles", std::move (roles_arr
));
1081 /* class sarif_location_manager : public sarif_object. */
1083 /* Base implementation of sarif_location_manager::add_related_location vfunc.
1085 Add LOCATION_OBJ to this object's "relatedLocations" array,
1086 creating it if it doesn't yet exist. */
1089 sarif_location_manager::
1090 add_related_location (std::unique_ptr
<sarif_location
> location_obj
,
1093 if (!m_related_locations_arr
)
1095 m_related_locations_arr
= new json::array ();
1096 /* Give ownership of m_related_locations_arr to json::object;
1097 keep a borrowed ptr. */
1098 set ("relatedLocations", m_related_locations_arr
);
1100 m_related_locations_arr
->append (std::move (location_obj
));
1104 sarif_location_manager::
1105 add_relationship_to_worklist (sarif_location
&location_obj
,
1106 enum worklist_item::kind kind
,
1109 m_worklist
.push_back (worklist_item (location_obj
,
1114 /* Process all items in this result's worklist.
1115 Doing so may temporarily add new items to the end
1117 Handling any item should be "lazy", and thus we should
1118 eventually drain the queue and terminate. */
1121 sarif_location_manager::process_worklist (sarif_builder
&builder
)
1123 while (!m_worklist
.empty ())
1125 const worklist_item
&item
= m_worklist
.front ();
1126 process_worklist_item (builder
, item
);
1127 m_worklist
.pop_front ();
1131 /* Process one item in this result's worklist, potentially
1132 adding new items to the end of the worklist. */
1135 sarif_location_manager::process_worklist_item (sarif_builder
&builder
,
1136 const worklist_item
&item
)
1138 switch (item
.m_kind
)
1142 case worklist_item::kind::included_from
:
1144 sarif_location
&included_loc_obj
= item
.m_location_obj
;
1145 sarif_location
*includer_loc_obj
= nullptr;
1146 auto iter
= m_included_from_locations
.find (item
.m_where
);
1147 if (iter
!= m_included_from_locations
.end ())
1148 includer_loc_obj
= iter
->second
;
1151 std::unique_ptr
<sarif_location
> new_loc_obj
1152 = builder
.make_location_object
1155 diagnostic_artifact_role::scanned_file
);
1156 includer_loc_obj
= new_loc_obj
.get ();
1157 add_related_location (std::move (new_loc_obj
), builder
);
1159 = std::pair
<location_t
, sarif_location
*> (item
.m_where
,
1161 m_included_from_locations
.insert (kv
);
1164 includer_loc_obj
->lazily_add_relationship
1166 location_relationship_kind::includes
,
1168 included_loc_obj
.lazily_add_relationship
1170 location_relationship_kind::is_included_by
,
1174 case worklist_item::kind::unlabelled_secondary_location
:
1176 sarif_location
&primary_loc_obj
= item
.m_location_obj
;
1177 sarif_location
*secondary_loc_obj
= nullptr;
1178 auto iter
= m_unlabelled_secondary_locations
.find (item
.m_where
);
1179 if (iter
!= m_unlabelled_secondary_locations
.end ())
1180 secondary_loc_obj
= iter
->second
;
1183 std::unique_ptr
<sarif_location
> new_loc_obj
1184 = builder
.make_location_object
1187 diagnostic_artifact_role::scanned_file
);
1188 secondary_loc_obj
= new_loc_obj
.get ();
1189 add_related_location (std::move (new_loc_obj
), builder
);
1191 = std::pair
<location_t
, sarif_location
*> (item
.m_where
,
1193 m_unlabelled_secondary_locations
.insert (kv
);
1195 gcc_assert (secondary_loc_obj
);
1196 primary_loc_obj
.lazily_add_relationship
1197 (*secondary_loc_obj
,
1198 location_relationship_kind::relevant
,
1205 /* class sarif_result : public sarif_location_manager. */
1207 /* Handle secondary diagnostics that occur within a diagnostic group.
1208 The closest SARIF seems to have to nested diagnostics is the
1209 "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
1210 so we lazily set this property and populate the array if and when
1211 secondary diagnostics occur (such as notes to a warning). */
1214 sarif_result::on_nested_diagnostic (const diagnostic_info
&diagnostic
,
1215 diagnostic_t
/*orig_diag_kind*/,
1216 sarif_builder
&builder
)
1218 /* We don't yet generate meaningful logical locations for notes;
1219 sometimes these will related to current_function_decl, but
1220 often they won't. */
1222 = builder
.make_location_object (*this, *diagnostic
.richloc
, nullptr,
1223 diagnostic_artifact_role::result_file
);
1225 = builder
.make_message_object (pp_formatted_text (builder
.get_printer ()));
1226 pp_clear_output_area (builder
.get_printer ());
1227 location_obj
->set
<sarif_message
> ("message", std::move (message_obj
));
1229 /* Add nesting level, as per "P3358R0 SARIF for Structured Diagnostics"
1230 https://wg21.link/P3358R0 */
1231 sarif_property_bag
&bag
= location_obj
->get_or_create_properties ();
1232 bag
.set_integer ("nestingLevel",
1233 builder
.get_context ().get_diagnostic_nesting_level ());
1235 add_related_location (std::move (location_obj
), builder
);
1238 /* Handle diagrams that occur within a diagnostic group.
1239 The closest thing in SARIF seems to be to add a location to the
1240 "releatedLocations" property (SARIF v2.1.0 section 3.27.22),
1241 and to put the diagram into the "message" property of that location
1242 (SARIF v2.1.0 section 3.28.5). */
1245 sarif_result::on_diagram (const diagnostic_diagram
&diagram
,
1246 sarif_builder
&builder
)
1248 auto location_obj
= ::make_unique
<sarif_location
> ();
1249 auto message_obj
= builder
.make_message_object_for_diagram (diagram
);
1250 location_obj
->set
<sarif_message
> ("message", std::move (message_obj
));
1252 add_related_location (std::move (location_obj
), builder
);
1255 /* class sarif_location : public sarif_object. */
1257 /* Ensure this location has an "id" and return it.
1258 Use LOC_MGR if an id needs to be allocated.
1260 See the "id" property (3.28.2).
1262 We use this to only assign ids to locations that are
1263 referenced by another sarif object; others have no "id". */
1266 sarif_location::lazily_add_id (sarif_location_manager
&loc_mgr
)
1268 long id
= get_id ();
1271 id
= loc_mgr
.allocate_location_id ();
1272 set_integer ("id", id
);
1273 gcc_assert (id
!= -1);
1277 /* Get the id of this location, or -1 if it doesn't have one. */
1280 sarif_location::get_id () const
1282 json::value
*id
= get ("id");
1285 gcc_assert (id
->get_kind () == json::JSON_INTEGER
);
1286 return static_cast <json::integer_number
*> (id
)->get ();
1289 // 3.34.3 kinds property
1291 get_string_for_location_relationship_kind (enum location_relationship_kind kind
)
1297 case location_relationship_kind::includes
:
1299 case location_relationship_kind::is_included_by
:
1300 return "isIncludedBy";
1301 case location_relationship_kind::relevant
:
1306 /* Lazily populate this location's "relationships" property (3.28.7)
1307 with the relationship of KIND to TARGET, creating objects
1309 Use LOC_MGR for any locations that need "id" values. */
1312 sarif_location::lazily_add_relationship (sarif_location
&target
,
1313 enum location_relationship_kind kind
,
1314 sarif_location_manager
&loc_mgr
)
1316 sarif_location_relationship
&relationship_obj
1317 = lazily_add_relationship_object (target
, loc_mgr
);
1319 relationship_obj
.lazily_add_kind (kind
);
1322 /* Lazily populate this location's "relationships" property (3.28.7)
1323 with a location_relationship to TARGET, creating objects
1325 Use LOC_MGR for any locations that need "id" values. */
1327 sarif_location_relationship
&
1328 sarif_location::lazily_add_relationship_object (sarif_location
&target
,
1329 sarif_location_manager
&loc_mgr
)
1331 /* See if THIS already has a locationRelationship referencing TARGET. */
1332 auto iter
= m_relationships_map
.find (&target
);
1333 if (iter
!= m_relationships_map
.end ())
1335 /* We already have a locationRelationship from THIS to TARGET. */
1336 sarif_location_relationship
*relationship
= iter
->second
;
1337 gcc_assert (relationship
->get_target_id() == target
.get_id ());
1338 return *relationship
;
1341 // Ensure that THIS has a "relationships" property (3.28.7).
1342 json::array
&relationships_arr
= lazily_add_relationships_array ();
1344 /* No existing locationRelationship from THIS to TARGET; make one,
1345 record it, and add it to the "relationships" array. */
1346 auto relationship_obj
1347 = ::make_unique
<sarif_location_relationship
> (target
, loc_mgr
);
1348 sarif_location_relationship
*relationship
= relationship_obj
.get ();
1350 = std::pair
<sarif_location
*,
1351 sarif_location_relationship
*> (&target
, relationship
);
1352 m_relationships_map
.insert (kv
);
1354 relationships_arr
.append (std::move (relationship_obj
));
1356 return *relationship
;
1359 /* Ensure this location has a "relationships" array (3.28.7). */
1362 sarif_location::lazily_add_relationships_array ()
1364 const char *const property_name
= "relationships";
1365 if (json::value
*relationships
= get (property_name
))
1367 gcc_assert (relationships
->get_kind () == json::JSON_ARRAY
);
1368 return *static_cast <json::array
*> (relationships
);
1370 json::array
*relationships_arr
= new json::array ();
1371 set (property_name
, relationships_arr
);
1372 return *relationships_arr
;
1375 /* class sarif_ice_notification : public sarif_location_manager. */
1377 /* sarif_ice_notification's ctor.
1378 DIAGNOSTIC is an internal compiler error. */
1380 sarif_ice_notification::
1381 sarif_ice_notification (const diagnostic_info
&diagnostic
,
1382 sarif_builder
&builder
,
1383 std::unique_ptr
<json::object
> backtrace
)
1385 /* "locations" property (SARIF v2.1.0 section 3.58.4). */
1387 = builder
.make_locations_arr (*this,
1389 diagnostic_artifact_role::result_file
);
1390 set
<json::array
> ("locations", std::move (locations_arr
));
1392 /* "message" property (SARIF v2.1.0 section 3.85.5). */
1394 = builder
.make_message_object (pp_formatted_text (builder
.get_printer ()));
1395 pp_clear_output_area (builder
.get_printer ());
1396 set
<sarif_message
> ("message", std::move (message_obj
));
1398 /* "level" property (SARIF v2.1.0 section 3.58.6). */
1399 set_string ("level", "error");
1401 /* If we have backtrace information, add it as part of a property bag. */
1404 sarif_property_bag
&bag
= get_or_create_properties ();
1405 bag
.set ("gcc/backtrace", std::move (backtrace
));
1409 /* Implementation of sarif_location_manager::add_related_location vfunc
1410 for notifications. */
1413 sarif_ice_notification::
1414 add_related_location (std::unique_ptr
<sarif_location
> location_obj
,
1415 sarif_builder
&builder
)
1417 /* Support for related locations within a notification was added
1418 in SARIF 2.2; see https://github.com/oasis-tcs/sarif-spec/issues/540 */
1419 if (builder
.get_version () >= sarif_version::v2_2_prerelease_2024_08_08
)
1420 sarif_location_manager::add_related_location (std::move (location_obj
),
1422 /* Otherwise implicitly discard LOCATION_OBJ. */
1425 /* class sarif_location_relationship : public sarif_object. */
1427 sarif_location_relationship::
1428 sarif_location_relationship (sarif_location
&target
,
1429 sarif_location_manager
&loc_mgr
)
1430 : m_kinds ((unsigned)location_relationship_kind::NUM_KINDS
)
1432 bitmap_clear (m_kinds
);
1433 set_integer ("target", target
.lazily_add_id (loc_mgr
));
1437 sarif_location_relationship::get_target_id () const
1439 json::value
*id
= get ("id");
1441 return static_cast <json::integer_number
*> (id
)->get ();
1445 sarif_location_relationship::
1446 lazily_add_kind (enum location_relationship_kind kind
)
1448 if (bitmap_bit_p (m_kinds
, (int)kind
))
1449 return; // already have this kind
1450 bitmap_set_bit (m_kinds
, (int)kind
);
1452 // 3.34.3 kinds property
1453 json::array
*kinds_arr
= nullptr;
1454 if (json::value
*kinds_val
= get ("kinds"))
1456 gcc_assert (kinds_val
->get_kind () == json::JSON_ARRAY
);
1460 kinds_arr
= new json::array ();
1461 set ("kinds", kinds_arr
);
1463 const char *kind_str
= get_string_for_location_relationship_kind (kind
);
1464 kinds_arr
->append_string (kind_str
);
1467 /* class sarif_code_flow : public sarif_object. */
1469 sarif_code_flow::sarif_code_flow (sarif_result
&parent
,
1470 unsigned idx_within_parent
)
1471 : m_parent (parent
),
1472 m_idx_within_parent (idx_within_parent
)
1474 /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
1475 auto thread_flows_arr
= ::make_unique
<json::array
> ();
1476 m_thread_flows_arr
= thread_flows_arr
.get (); // borrowed
1477 set
<json::array
> ("threadFlows", std::move (thread_flows_arr
));
1481 sarif_code_flow::get_or_append_thread_flow (const diagnostic_thread
&thread
,
1482 diagnostic_thread_id_t thread_id
)
1484 sarif_thread_flow
**slot
= m_thread_id_map
.get (thread_id
);
1488 unsigned next_thread_flow_idx
= m_thread_flows_arr
->size ();
1489 auto thread_flow_obj
1490 = ::make_unique
<sarif_thread_flow
> (*this, thread
, next_thread_flow_idx
);
1491 m_thread_id_map
.put (thread_id
, thread_flow_obj
.get ()); // borrowed
1492 sarif_thread_flow
*result
= thread_flow_obj
.get ();
1493 m_thread_flows_arr
->append
<sarif_thread_flow
> (std::move (thread_flow_obj
));
1498 sarif_code_flow::get_thread_flow (diagnostic_thread_id_t thread_id
)
1500 sarif_thread_flow
**slot
= m_thread_id_map
.get (thread_id
);
1501 gcc_assert (slot
); // it must already have one
1506 sarif_code_flow::add_location (sarif_thread_flow_location
&tfl_obj
)
1508 m_all_tfl_objs
.push_back (&tfl_obj
);
1511 sarif_thread_flow_location
&
1512 sarif_code_flow::get_thread_flow_loc_obj (diagnostic_event_id_t event_id
) const
1514 gcc_assert (event_id
.known_p ());
1515 gcc_assert ((size_t)event_id
.zero_based () < m_all_tfl_objs
.size ());
1516 sarif_thread_flow_location
*tfl_obj
= m_all_tfl_objs
[event_id
.zero_based ()];
1517 gcc_assert (tfl_obj
);
1521 /* class sarif_thread_flow : public sarif_object. */
1523 sarif_thread_flow::sarif_thread_flow (sarif_code_flow
&parent
,
1524 const diagnostic_thread
&thread
,
1525 unsigned idx_within_parent
)
1526 : m_parent (parent
),
1527 m_idx_within_parent (idx_within_parent
)
1529 /* "id" property (SARIF v2.1.0 section 3.37.2). */
1530 label_text
name (thread
.get_name (false));
1531 set_string ("id", name
.get ());
1533 /* "locations" property (SARIF v2.1.0 section 3.37.6). */
1534 m_locations_arr
= new json::array ();
1536 /* Give ownership of m_locations_arr to json::object;
1537 keep a borrowed ptr. */
1538 set ("locations", m_locations_arr
);
1541 /* Add a sarif_thread_flow_location to this threadFlow object, but
1542 don't populate it yet. */
1544 sarif_thread_flow_location
&
1545 sarif_thread_flow::add_location ()
1547 const unsigned thread_flow_location_idx
= m_locations_arr
->size ();
1548 sarif_thread_flow_location
*thread_flow_loc_obj
1549 = new sarif_thread_flow_location (*this, thread_flow_location_idx
);
1550 m_locations_arr
->append (thread_flow_loc_obj
);
1551 m_parent
.add_location (*thread_flow_loc_obj
);
1552 return *thread_flow_loc_obj
;
1555 /* class sarif_builder. */
1557 /* sarif_builder's ctor. */
1559 sarif_builder::sarif_builder (diagnostic_context
&context
,
1560 pretty_printer
&printer
,
1561 const line_maps
*line_maps
,
1562 const char *main_input_filename_
,
1564 enum sarif_version version
)
1565 : m_context (context
),
1566 m_printer (&printer
),
1567 m_line_maps (line_maps
),
1568 m_token_printer (*this),
1569 m_version (version
),
1571 (::make_unique
<sarif_invocation
> (*this,
1572 context
.get_original_argv ())),
1573 m_results_array (new json::array ()),
1574 m_cur_group_result (nullptr),
1575 m_seen_any_relative_paths (false),
1577 m_rules_arr (new json::array ()),
1578 m_tabstop (context
.m_tabstop
),
1579 m_formatted (formatted
),
1580 m_next_result_idx (0),
1581 m_current_code_flow (nullptr)
1583 gcc_assert (m_line_maps
);
1585 /* Mark MAIN_INPUT_FILENAME_ as the artifact that the tool was
1587 Only quote the contents if it gets referenced by physical locations,
1588 since otherwise the "no diagnostics" case would quote the main input
1589 file, and doing so noticeably bloated the output seen in analyzer
1590 integration testing (build directory went from 20G -> 21G). */
1591 if (main_input_filename_
)
1592 get_or_create_artifact (main_input_filename_
,
1593 diagnostic_artifact_role::analysis_target
,
1597 sarif_builder::~sarif_builder ()
1599 /* Normally m_filename_to_artifact_map will have been emptied as part
1600 of make_run_object, but this isn't run by all the selftests.
1601 Ensure the artifact objects are cleaned up for such cases. */
1602 for (auto iter
: m_filename_to_artifact_map
)
1604 sarif_artifact
*artifact_obj
= iter
.second
;
1605 delete artifact_obj
;
1609 /* Functions at which to stop the backtrace print. It's not
1610 particularly helpful to print the callers of these functions. */
1612 static const char * const bt_stop
[] =
1622 bt_closure (sarif_builder
&builder
,
1623 json::array
*frames_arr
)
1624 : m_builder (builder
),
1625 m_frames_arr (frames_arr
)
1629 sarif_builder
&m_builder
;
1630 json::array
*m_frames_arr
;
1633 /* A callback function passed to the backtrace_full function. */
1636 bt_callback (void *data
, uintptr_t pc
, const char *filename
, int lineno
,
1637 const char *function
)
1639 bt_closure
*closure
= (bt_closure
*)data
;
1641 /* If we don't have any useful information, don't print
1643 if (filename
== NULL
&& function
== NULL
)
1646 /* Skip functions in diagnostic.cc or diagnostic-global-context.cc. */
1647 if (closure
->m_frames_arr
->size () == 0
1649 && (strcmp (lbasename (filename
), "diagnostic.cc") == 0
1650 || strcmp (lbasename (filename
),
1651 "diagnostic-global-context.cc") == 0))
1654 /* Print up to 20 functions. We could make this a --param, but
1655 since this is only for debugging just use a constant for now. */
1656 if (closure
->m_frames_arr
->size () >= 20)
1658 /* Returning a non-zero value stops the backtrace. */
1663 if (function
!= NULL
)
1665 char *str
= cplus_demangle_v3 (function
,
1666 (DMGL_VERBOSE
| DMGL_ANSI
1667 | DMGL_GNU_V3
| DMGL_PARAMS
));
1674 for (size_t i
= 0; i
< ARRAY_SIZE (bt_stop
); ++i
)
1676 size_t len
= strlen (bt_stop
[i
]);
1677 if (strncmp (function
, bt_stop
[i
], len
) == 0
1678 && (function
[len
] == '\0' || function
[len
] == '('))
1682 /* Returning a non-zero value stops the backtrace. */
1688 auto frame_obj
= ::make_unique
<json::object
> ();
1690 /* I tried using sarifStack and sarifStackFrame for this
1691 but it's not a good fit e.g. PC information. */
1693 snprintf (buf
, sizeof (buf
) - 1, "0x%lx", (unsigned long)pc
);
1694 frame_obj
->set_string ("pc", buf
);
1696 frame_obj
->set_string ("function", function
);
1698 frame_obj
->set_string ("filename", filename
);
1699 frame_obj
->set_integer ("lineno", lineno
);
1700 closure
->m_frames_arr
->append (std::move (frame_obj
));
1708 /* Attempt to generate a JSON object representing a backtrace,
1709 for adding to ICE notifications. */
1711 std::unique_ptr
<json::object
>
1712 sarif_builder::make_stack_from_backtrace ()
1714 auto frames_arr
= ::make_unique
<json::array
> ();
1716 backtrace_state
*state
= nullptr;
1717 state
= backtrace_create_state (nullptr, 0, nullptr, nullptr);
1718 bt_closure
closure (*this, frames_arr
.get ());
1719 const int frames_to_skip
= 5;
1720 if (state
!= nullptr)
1721 backtrace_full (state
, frames_to_skip
, bt_callback
, nullptr,
1724 if (frames_arr
->size () == 0)
1727 auto stack
= ::make_unique
<json::object
> ();
1728 stack
->set ("frames", std::move (frames_arr
));
1732 /* Implementation of "on_report_diagnostic" for SARIF output. */
1735 sarif_builder::on_report_diagnostic (const diagnostic_info
&diagnostic
,
1736 diagnostic_t orig_diag_kind
,
1737 diagnostic_sarif_format_buffer
*buffer
)
1739 pp_output_formatted_text (m_printer
, m_context
.get_urlifier ());
1741 if (diagnostic
.kind
== DK_ICE
|| diagnostic
.kind
== DK_ICE_NOBT
)
1743 std::unique_ptr
<json::object
> stack
= make_stack_from_backtrace ();
1744 m_invocation_obj
->add_notification_for_ice (diagnostic
, *this,
1747 /* Print a header for the remaining output to stderr, and
1748 return, attempting to print the usual ICE messages to
1749 stderr. Hopefully this will be helpful to the user in
1750 indicating what's gone wrong (also for DejaGnu, for pruning
1752 fnotice (stderr
, "Internal compiler error:\n");
1759 /* When buffering, we can only handle top-level results. */
1760 gcc_assert (!m_cur_group_result
);
1761 buffer
->add_result (make_result_object (diagnostic
, orig_diag_kind
,
1762 m_next_result_idx
++));
1766 if (m_cur_group_result
)
1767 /* Nested diagnostic. */
1768 m_cur_group_result
->on_nested_diagnostic (diagnostic
,
1773 /* Top-level diagnostic. */
1774 m_cur_group_result
= make_result_object (diagnostic
, orig_diag_kind
,
1775 m_next_result_idx
++);
1779 /* Implementation of diagnostic_context::m_diagrams.m_emission_cb
1780 for SARIF output. */
1783 sarif_builder::emit_diagram (const diagnostic_diagram
&diagram
)
1785 /* We must be within the emission of a top-level diagnostic. */
1786 gcc_assert (m_cur_group_result
);
1787 m_cur_group_result
->on_diagram (diagram
, *this);
1790 /* Implementation of "end_group_cb" for SARIF output. */
1793 sarif_builder::end_group ()
1795 if (m_cur_group_result
)
1797 m_cur_group_result
->process_worklist (*this);
1798 m_results_array
->append
<sarif_result
> (std::move (m_cur_group_result
));
1802 /* Create a top-level object, and add it to all the results
1803 (and other entities) we've seen so far, moving ownership
1806 std::unique_ptr
<sarif_log
>
1807 sarif_builder::flush_to_object ()
1809 m_invocation_obj
->prepare_to_flush (*this);
1810 std::unique_ptr
<sarif_log
> top
1811 = make_top_level_object (std::move (m_invocation_obj
),
1812 std::move (m_results_array
));
1816 /* Create a top-level object, and add it to all the results
1817 (and other entities) we've seen so far.
1819 Flush it all to OUTF. */
1822 sarif_builder::flush_to_file (FILE *outf
)
1824 std::unique_ptr
<sarif_log
> top
= flush_to_object ();
1825 top
->dump (outf
, m_formatted
);
1826 fprintf (outf
, "\n");
1829 /* Attempt to convert DIAG_KIND to a suitable value for the "level"
1830 property (SARIF v2.1.0 section 3.27.10).
1832 Return nullptr if there isn't one. */
1835 maybe_get_sarif_level (diagnostic_t diag_kind
)
1844 case DK_ANACHRONISM
:
1851 /* Make a string for DIAG_KIND suitable for use a ruleId
1852 (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
1853 have anything better to use. */
1856 make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind
)
1858 /* Lose the trailing ": ". */
1859 const char *kind_text
= get_diagnostic_kind_text (diag_kind
);
1860 size_t len
= strlen (kind_text
);
1861 gcc_assert (len
> 2);
1862 gcc_assert (kind_text
[len
- 2] == ':');
1863 gcc_assert (kind_text
[len
- 1] == ' ');
1864 char *rstrip
= xstrdup (kind_text
);
1865 rstrip
[len
- 2] = '\0';
1869 /* Make a "result" object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
1871 std::unique_ptr
<sarif_result
>
1872 sarif_builder::make_result_object (const diagnostic_info
&diagnostic
,
1873 diagnostic_t orig_diag_kind
,
1874 unsigned idx_within_parent
)
1876 auto result_obj
= ::make_unique
<sarif_result
> (idx_within_parent
);
1878 /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
1879 /* Ideally we'd have an option_name for these. */
1880 if (char *option_text
1881 = m_context
.make_option_name (diagnostic
.option_id
,
1882 orig_diag_kind
, diagnostic
.kind
))
1884 /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
1885 Set ruleId referencing them. */
1886 result_obj
->set_string ("ruleId", option_text
);
1887 if (m_rule_id_set
.contains (option_text
))
1891 /* This is the first time we've seen this ruleId. */
1892 /* Add to set, taking ownership. */
1893 m_rule_id_set
.add (option_text
);
1895 m_rules_arr
->append
<sarif_reporting_descriptor
>
1896 (make_reporting_descriptor_object_for_warning (diagnostic
,
1903 /* Otherwise, we have an "error" or a stray "note"; use the
1904 diagnostic kind as the ruleId, so that the result object at least
1906 We don't bother creating reportingDescriptor objects for these. */
1907 char *rule_id
= make_rule_id_for_diagnostic_kind (orig_diag_kind
);
1908 result_obj
->set_string ("ruleId", rule_id
);
1912 if (diagnostic
.metadata
)
1914 /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
1915 if (int cwe_id
= diagnostic
.metadata
->get_cwe ())
1917 auto taxa_arr
= ::make_unique
<json::array
> ();
1918 taxa_arr
->append
<sarif_reporting_descriptor_reference
>
1919 (make_reporting_descriptor_reference_object_for_cwe_id (cwe_id
));
1920 result_obj
->set
<json::array
> ("taxa", std::move (taxa_arr
));
1923 diagnostic
.metadata
->maybe_add_sarif_properties (*result_obj
);
1925 /* We don't yet support diagnostic_metadata::rule. */
1928 /* "level" property (SARIF v2.1.0 section 3.27.10). */
1929 if (const char *sarif_level
= maybe_get_sarif_level (diagnostic
.kind
))
1930 result_obj
->set_string ("level", sarif_level
);
1932 /* "message" property (SARIF v2.1.0 section 3.27.11). */
1934 = make_message_object (pp_formatted_text (m_printer
));
1935 pp_clear_output_area (m_printer
);
1936 result_obj
->set
<sarif_message
> ("message", std::move (message_obj
));
1938 /* "locations" property (SARIF v2.1.0 section 3.27.12). */
1939 result_obj
->set
<json::array
>
1941 make_locations_arr (*result_obj
.get (),
1943 diagnostic_artifact_role::result_file
));
1945 /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
1946 if (const diagnostic_path
*path
= diagnostic
.richloc
->get_path ())
1948 auto code_flows_arr
= ::make_unique
<json::array
> ();
1949 const unsigned code_flow_index
= 0;
1950 code_flows_arr
->append
<sarif_code_flow
>
1951 (make_code_flow_object (*result_obj
.get (),
1954 result_obj
->set
<json::array
> ("codeFlows", std::move (code_flows_arr
));
1957 /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
1958 set up later, if any nested diagnostics occur within this diagnostic
1961 /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
1962 const rich_location
*richloc
= diagnostic
.richloc
;
1963 if (richloc
->get_num_fixit_hints ())
1965 auto fix_arr
= ::make_unique
<json::array
> ();
1966 fix_arr
->append
<sarif_fix
> (make_fix_object (*richloc
));
1967 result_obj
->set
<json::array
> ("fixes", std::move (fix_arr
));
1973 /* Make a "reportingDescriptor" object (SARIF v2.1.0 section 3.49)
1974 for a GCC warning. */
1976 std::unique_ptr
<sarif_reporting_descriptor
>
1978 make_reporting_descriptor_object_for_warning (const diagnostic_info
&diagnostic
,
1979 diagnostic_t
/*orig_diag_kind*/,
1980 const char *option_text
)
1982 auto reporting_desc
= ::make_unique
<sarif_reporting_descriptor
> ();
1984 /* "id" property (SARIF v2.1.0 section 3.49.3). */
1985 reporting_desc
->set_string ("id", option_text
);
1987 /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
1988 it seems redundant compared to "id". */
1990 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
1991 if (char *option_url
= m_context
.make_option_url (diagnostic
.option_id
))
1993 reporting_desc
->set_string ("helpUri", option_url
);
1997 return reporting_desc
;
2000 /* Make a "reportingDescriptor" object (SARIF v2.1.0 section 3.49)
2001 for CWE_ID, for use within the CWE taxa array. */
2003 std::unique_ptr
<sarif_reporting_descriptor
>
2004 sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id
) const
2006 auto reporting_desc
= ::make_unique
<sarif_reporting_descriptor
> ();
2008 /* "id" property (SARIF v2.1.0 section 3.49.3). */
2011 pp_printf (&pp
, "%i", cwe_id
);
2012 reporting_desc
->set_string ("id", pp_formatted_text (&pp
));
2015 /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
2017 char *url
= get_cwe_url (cwe_id
);
2018 reporting_desc
->set_string ("helpUri", url
);
2022 return reporting_desc
;
2025 /* Make a "reportingDescriptorReference" object (SARIF v2.1.0 section 3.52)
2026 referencing CWE_ID, for use within a result object.
2027 Also, add CWE_ID to m_cwe_id_set. */
2029 std::unique_ptr
<sarif_reporting_descriptor_reference
>
2031 make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id
)
2033 auto desc_ref_obj
= ::make_unique
<sarif_reporting_descriptor_reference
> ();
2035 /* "id" property (SARIF v2.1.0 section 3.52.4). */
2038 pp_printf (&pp
, "%i", cwe_id
);
2039 desc_ref_obj
->set_string ("id", pp_formatted_text (&pp
));
2042 /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
2043 desc_ref_obj
->set
<sarif_tool_component_reference
>
2044 ("toolComponent", make_tool_component_reference_object_for_cwe ());
2046 /* Add CWE_ID to our set. */
2047 gcc_assert (cwe_id
> 0);
2048 m_cwe_id_set
.add (cwe_id
);
2050 return desc_ref_obj
;
2053 /* Make a "toolComponentReference" object (SARIF v2.1.0 section 3.54) that
2054 references the CWE taxonomy. */
2056 std::unique_ptr
<sarif_tool_component_reference
>
2058 make_tool_component_reference_object_for_cwe () const
2060 auto comp_ref_obj
= ::make_unique
<sarif_tool_component_reference
> ();
2062 /* "name" property (SARIF v2.1.0 section 3.54.3). */
2063 comp_ref_obj
->set_string ("name", "cwe");
2065 return comp_ref_obj
;
2068 /* Make an array suitable for use as the "locations" property of:
2069 - a "result" object (SARIF v2.1.0 section 3.27.12), or
2070 - a "notification" object (SARIF v2.1.0 section 3.58.4).
2071 Use LOC_MGR for any locations that need "id" values. */
2073 std::unique_ptr
<json::array
>
2074 sarif_builder::make_locations_arr (sarif_location_manager
&loc_mgr
,
2075 const diagnostic_info
&diagnostic
,
2076 enum diagnostic_artifact_role role
)
2078 auto locations_arr
= ::make_unique
<json::array
> ();
2079 const logical_location
*logical_loc
= nullptr;
2080 if (auto client_data_hooks
= m_context
.get_client_data_hooks ())
2081 logical_loc
= client_data_hooks
->get_current_logical_location ();
2084 = make_location_object (loc_mgr
, *diagnostic
.richloc
, logical_loc
, role
);
2085 /* Don't add entirely empty location objects to the array. */
2086 if (!location_obj
->is_empty ())
2087 locations_arr
->append
<sarif_location
> (std::move (location_obj
));
2089 return locations_arr
;
2092 /* If LOGICAL_LOC is non-null, use it to create a "logicalLocations" property
2093 within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4). */
2097 set_any_logical_locs_arr (sarif_location
&location_obj
,
2098 const logical_location
*logical_loc
)
2102 auto location_locs_arr
= ::make_unique
<json::array
> ();
2103 location_locs_arr
->append
<sarif_logical_location
>
2104 (make_sarif_logical_location_object (*logical_loc
));
2105 location_obj
.set
<json::array
> ("logicalLocations",
2106 std::move (location_locs_arr
));
2109 /* Make a "location" object (SARIF v2.1.0 section 3.28) for RICH_LOC
2111 Use LOC_MGR for any locations that need "id" values, and for
2112 any worklist items. */
2114 std::unique_ptr
<sarif_location
>
2115 sarif_builder::make_location_object (sarif_location_manager
&loc_mgr
,
2116 const rich_location
&rich_loc
,
2117 const logical_location
*logical_loc
,
2118 enum diagnostic_artifact_role role
)
2120 class escape_nonascii_renderer
: public content_renderer
2123 escape_nonascii_renderer (const rich_location
&richloc
,
2124 enum diagnostics_escape_format escape_format
)
2125 : m_richloc (richloc
),
2126 m_escape_format (escape_format
)
2129 std::unique_ptr
<sarif_multiformat_message_string
>
2130 render (const sarif_builder
&builder
) const final override
2132 diagnostic_context dc
;
2133 diagnostic_initialize (&dc
, 0);
2134 dc
.m_source_printing
.enabled
= true;
2135 dc
.m_source_printing
.colorize_source_p
= false;
2136 dc
.m_source_printing
.show_labels_p
= true;
2137 dc
.m_source_printing
.show_line_numbers_p
= true;
2139 rich_location
my_rich_loc (m_richloc
);
2140 my_rich_loc
.set_escape_on_output (true);
2142 diagnostic_source_print_policy
source_policy (dc
);
2143 dc
.set_escape_format (m_escape_format
);
2144 diagnostic_text_output_format
text_output (dc
);
2145 source_policy
.print (*text_output
.get_printer (),
2146 my_rich_loc
, DK_ERROR
, nullptr);
2148 const char *buf
= pp_formatted_text (text_output
.get_printer ());
2149 std::unique_ptr
<sarif_multiformat_message_string
> result
2150 = builder
.make_multiformat_message_string (buf
);
2152 diagnostic_finish (&dc
);
2157 const rich_location
&m_richloc
;
2158 enum diagnostics_escape_format m_escape_format
;
2159 } the_renderer (rich_loc
,
2160 m_context
.get_escape_format ());
2162 auto location_obj
= ::make_unique
<sarif_location
> ();
2164 /* Get primary loc from RICH_LOC. */
2165 location_t loc
= rich_loc
.get_loc ();
2167 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
2168 const content_renderer
*snippet_renderer
2169 = rich_loc
.escape_on_output_p () ? &the_renderer
: nullptr;
2170 if (auto phs_loc_obj
2171 = maybe_make_physical_location_object (loc
, role
,
2172 rich_loc
.get_column_override (),
2174 location_obj
->set
<sarif_physical_location
> ("physicalLocation",
2175 std::move (phs_loc_obj
));
2177 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
2178 set_any_logical_locs_arr (*location_obj
, logical_loc
);
2180 /* Handle labelled ranges and/or secondary locations. */
2182 std::unique_ptr
<json::array
> annotations_arr
= nullptr;
2183 for (unsigned int i
= 0; i
< rich_loc
.get_num_locations (); i
++)
2185 const location_range
*range
= rich_loc
.get_range (i
);
2186 bool handled
= false;
2187 if (const range_label
*label
= range
->m_label
)
2189 label_text text
= label
->get_text (i
);
2192 /* Create annotations for any labelled ranges. */
2193 location_t range_loc
= rich_loc
.get_loc (i
);
2195 = maybe_make_region_object (range_loc
,
2196 rich_loc
.get_column_override ());
2199 if (!annotations_arr
)
2200 annotations_arr
= ::make_unique
<json::array
> ();
2201 region
->set
<sarif_message
>
2202 ("message", make_message_object (text
.get ()));
2203 annotations_arr
->append
<sarif_region
> (std::move (region
));
2209 /* Add related locations for any secondary locations in RICH_LOC
2210 that don't have labels (and thus aren't added to "annotations"). */
2211 if (i
> 0 && !handled
)
2212 loc_mgr
.add_relationship_to_worklist
2213 (*location_obj
.get (),
2214 sarif_location_manager::worklist_item::kind::unlabelled_secondary_location
,
2217 if (annotations_arr
)
2218 /* "annotations" property (SARIF v2.1.0 section 3.28.6). */
2219 location_obj
->set
<json::array
> ("annotations",
2220 std::move (annotations_arr
));
2223 add_any_include_chain (loc_mgr
, *location_obj
.get (), loc
);
2225 /* A flag for hinting that the diagnostic involves issues at the
2226 level of character encodings (such as homoglyphs, or misleading
2227 bidirectional control codes), and thus that it will be helpful
2228 to the user if we show some representation of
2229 how the characters in the pertinent source lines are encoded. */
2230 if (rich_loc
.escape_on_output_p ())
2232 sarif_property_bag
&bag
= location_obj
->get_or_create_properties ();
2233 bag
.set_bool ("gcc/escapeNonAscii", rich_loc
.escape_on_output_p ());
2236 return location_obj
;
2239 /* If WHERE was #included from somewhere, add a worklist item
2240 to LOC_MGR to lazily add a location for the #include location,
2241 and relationships between it and the LOCATION_OBJ.
2242 Compare with diagnostic_context::report_current_module, but rather
2243 than iterating the current chain, we add the next edge and iterate
2244 in the worklist, so that edges are only added once. */
2247 sarif_builder::add_any_include_chain (sarif_location_manager
&loc_mgr
,
2248 sarif_location
&location_obj
,
2251 if (where
<= BUILTINS_LOCATION
)
2254 const line_map_ordinary
*map
= nullptr;
2255 linemap_resolve_location (m_line_maps
, where
,
2256 LRK_MACRO_DEFINITION_LOCATION
,
2262 location_t include_loc
= linemap_included_from (map
);
2263 map
= linemap_included_from_linemap (m_line_maps
, map
);
2266 loc_mgr
.add_relationship_to_worklist
2268 sarif_result::worklist_item::kind::included_from
,
2272 /* Make a "location" object (SARIF v2.1.0 section 3.28) for WHERE
2273 within an include chain. */
2275 std::unique_ptr
<sarif_location
>
2276 sarif_builder::make_location_object (sarif_location_manager
&loc_mgr
,
2278 enum diagnostic_artifact_role role
)
2280 auto location_obj
= ::make_unique
<sarif_location
> ();
2282 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
2283 if (auto phs_loc_obj
2284 = maybe_make_physical_location_object (loc
, role
, 0, nullptr))
2285 location_obj
->set
<sarif_physical_location
> ("physicalLocation",
2286 std::move (phs_loc_obj
));
2288 add_any_include_chain (loc_mgr
, *location_obj
.get (), loc
);
2290 return location_obj
;
2293 /* Make a "location" object (SARIF v2.1.0 section 3.28) for EVENT
2294 within a diagnostic_path. */
2296 std::unique_ptr
<sarif_location
>
2297 sarif_builder::make_location_object (sarif_location_manager
&loc_mgr
,
2298 const diagnostic_event
&event
,
2299 enum diagnostic_artifact_role role
)
2301 auto location_obj
= ::make_unique
<sarif_location
> ();
2303 /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
2304 location_t loc
= event
.get_location ();
2305 if (auto phs_loc_obj
2306 = maybe_make_physical_location_object (loc
, role
, 0, nullptr))
2307 location_obj
->set
<sarif_physical_location
> ("physicalLocation",
2308 std::move (phs_loc_obj
));
2310 /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
2311 const logical_location
*logical_loc
= event
.get_logical_location ();
2312 set_any_logical_locs_arr (*location_obj
, logical_loc
);
2314 /* "message" property (SARIF v2.1.0 section 3.28.5). */
2315 std::unique_ptr
<pretty_printer
> pp
= get_printer ()->clone ();
2316 event
.print_desc (*pp
);
2317 location_obj
->set
<sarif_message
>
2319 make_message_object (pp_formatted_text (pp
.get ())));
2321 add_any_include_chain (loc_mgr
, *location_obj
.get (), loc
);
2323 return location_obj
;
2326 /* Make a "physicalLocation" object (SARIF v2.1.0 section 3.29) for LOC.
2328 If COLUMN_OVERRIDE is non-zero, then use it as the column number
2329 if LOC has no column information.
2331 Ensure that we have an artifact object for the file, adding ROLE to it,
2332 and flagging that we will attempt to embed the contents of the artifact
2333 when writing it out. */
2335 std::unique_ptr
<sarif_physical_location
>
2337 maybe_make_physical_location_object (location_t loc
,
2338 enum diagnostic_artifact_role role
,
2339 int column_override
,
2340 const content_renderer
*snippet_renderer
)
2342 if (loc
<= BUILTINS_LOCATION
|| LOCATION_FILE (loc
) == nullptr)
2345 auto phys_loc_obj
= ::make_unique
<sarif_physical_location
> ();
2347 /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
2348 phys_loc_obj
->set
<sarif_artifact_location
>
2349 ("artifactLocation", make_artifact_location_object (loc
));
2350 get_or_create_artifact (LOCATION_FILE (loc
), role
, true);
2352 /* "region" property (SARIF v2.1.0 section 3.29.4). */
2353 if (auto region_obj
= maybe_make_region_object (loc
, column_override
))
2354 phys_loc_obj
->set
<sarif_region
> ("region", std::move (region_obj
));
2356 /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
2357 if (auto context_region_obj
2358 = maybe_make_region_object_for_context (loc
, snippet_renderer
))
2359 phys_loc_obj
->set
<sarif_region
> ("contextRegion",
2360 std::move (context_region_obj
));
2362 /* Instead, we add artifacts to the run as a whole,
2363 with artifact.contents.
2364 Could do both, though. */
2366 return phys_loc_obj
;
2369 /* Make an "artifactLocation" object (SARIF v2.1.0 section 3.4) for LOC,
2370 or return nullptr. */
2372 std::unique_ptr
<sarif_artifact_location
>
2373 sarif_builder::make_artifact_location_object (location_t loc
)
2375 return make_artifact_location_object (LOCATION_FILE (loc
));
2378 /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
2379 for when we need to express paths relative to PWD. */
2381 #define PWD_PROPERTY_NAME ("PWD")
2383 /* Make an "artifactLocation" object (SARIF v2.1.0 section 3.4) for FILENAME,
2384 or return nullptr. */
2386 std::unique_ptr
<sarif_artifact_location
>
2387 sarif_builder::make_artifact_location_object (const char *filename
)
2389 auto artifact_loc_obj
= ::make_unique
<sarif_artifact_location
> ();
2391 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
2392 artifact_loc_obj
->set_string ("uri", filename
);
2394 if (filename
[0] != '/')
2396 /* If we have a relative path, set the "uriBaseId" property
2397 (SARIF v2.1.0 section 3.4.4). */
2398 artifact_loc_obj
->set_string ("uriBaseId", PWD_PROPERTY_NAME
);
2399 m_seen_any_relative_paths
= true;
2402 return artifact_loc_obj
;
2405 /* Get the PWD, or nullptr, as an absolute file-based URI,
2406 adding a trailing forward slash (as required by SARIF v2.1.0
2407 section 3.14.14). */
2412 /* The prefix of a file-based URI, up to, but not including the path. */
2413 #define FILE_PREFIX ("file://")
2415 const char *pwd
= getpwd ();
2418 size_t len
= strlen (pwd
);
2419 if (len
== 0 || pwd
[len
- 1] != '/')
2420 return concat (FILE_PREFIX
, pwd
, "/", nullptr);
2423 gcc_assert (pwd
[len
- 1] == '/');
2424 return concat (FILE_PREFIX
, pwd
, nullptr);
2428 /* Make an "artifactLocation" object (SARIF v2.1.0 section 3.4) for the pwd,
2429 for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
2430 section 3.14.14) when we have any relative paths. */
2432 std::unique_ptr
<sarif_artifact_location
>
2433 sarif_builder::make_artifact_location_object_for_pwd () const
2435 auto artifact_loc_obj
= ::make_unique
<sarif_artifact_location
> ();
2437 /* "uri" property (SARIF v2.1.0 section 3.4.3). */
2438 if (char *pwd
= make_pwd_uri_str ())
2440 gcc_assert (strlen (pwd
) > 0);
2441 gcc_assert (pwd
[strlen (pwd
) - 1] == '/');
2442 artifact_loc_obj
->set_string ("uri", pwd
);
2446 return artifact_loc_obj
;
2449 /* Get the column number within EXPLOC. */
2452 sarif_builder::get_sarif_column (expanded_location exploc
) const
2454 cpp_char_column_policy
policy (m_tabstop
, cpp_wcwidth
);
2455 return location_compute_display_column (m_context
.get_file_cache (),
2459 /* Make a "region" object (SARIF v2.1.0 section 3.30) for LOC,
2462 If COLUMN_OVERRIDE is non-zero, then use it as the column number
2463 if LOC has no column information.
2465 We only support text properties of regions ("text regions"),
2466 not binary properties ("binary regions"); see 3.30.1. */
2468 std::unique_ptr
<sarif_region
>
2469 sarif_builder::maybe_make_region_object (location_t loc
,
2470 int column_override
) const
2472 location_t caret_loc
= get_pure_location (loc
);
2474 if (caret_loc
<= BUILTINS_LOCATION
)
2477 location_t start_loc
= get_start (loc
);
2478 location_t finish_loc
= get_finish (loc
);
2480 expanded_location exploc_caret
= expand_location (caret_loc
);
2481 expanded_location exploc_start
= expand_location (start_loc
);
2482 expanded_location exploc_finish
= expand_location (finish_loc
);
2484 if (exploc_start
.file
!=exploc_caret
.file
)
2486 if (exploc_finish
.file
!=exploc_caret
.file
)
2489 /* We can have line == 0 in the presence of "#" lines.
2490 SARIF requires lines > 0, so if we hit this case we don't have a
2491 way of validly representing the region as SARIF; bail out. */
2492 if (exploc_start
.line
<= 0)
2495 auto region_obj
= ::make_unique
<sarif_region
> ();
2497 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
2498 region_obj
->set_integer ("startLine", exploc_start
.line
);
2500 /* "startColumn" property (SARIF v2.1.0 section 3.30.6).
2502 We use column == 0 to mean the whole line, so omit the column
2503 information for this case, unless COLUMN_OVERRIDE is non-zero,
2504 (for handling certain awkward lexer diagnostics) */
2506 if (exploc_start
.column
== 0 && column_override
)
2507 /* Use the provided column number. */
2508 exploc_start
.column
= column_override
;
2510 if (exploc_start
.column
> 0)
2512 int start_column
= get_sarif_column (exploc_start
);
2513 region_obj
->set_integer ("startColumn", start_column
);
2516 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
2517 if (exploc_finish
.line
!= exploc_start
.line
2518 && exploc_finish
.line
> 0)
2519 region_obj
->set_integer ("endLine", exploc_finish
.line
);
2521 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
2522 This expresses the column immediately beyond the range.
2524 We use column == 0 to mean the whole line, so omit the column
2525 information for this case. */
2526 if (exploc_finish
.column
> 0)
2528 int next_column
= get_sarif_column (exploc_finish
) + 1;
2529 region_obj
->set_integer ("endColumn", next_column
);
2535 /* Make a "region" object (SARIF v2.1.0 section 3.30) for the "contextRegion"
2536 property (SARIF v2.1.0 section 3.29.5) of a "physicalLocation".
2538 This is similar to maybe_make_region_object, but ignores column numbers,
2539 covering the line(s) as a whole, and including a "snippet" property
2540 embedding those source lines, making it easier for consumers to show
2541 the pertinent source. */
2543 std::unique_ptr
<sarif_region
>
2545 maybe_make_region_object_for_context (location_t loc
,
2546 const content_renderer
*snippet_renderer
)
2549 location_t caret_loc
= get_pure_location (loc
);
2551 if (caret_loc
<= BUILTINS_LOCATION
)
2554 location_t start_loc
= get_start (loc
);
2555 location_t finish_loc
= get_finish (loc
);
2557 expanded_location exploc_caret
= expand_location (caret_loc
);
2558 expanded_location exploc_start
= expand_location (start_loc
);
2559 expanded_location exploc_finish
= expand_location (finish_loc
);
2561 if (exploc_start
.file
!=exploc_caret
.file
)
2563 if (exploc_finish
.file
!=exploc_caret
.file
)
2566 /* We can have line == 0 in the presence of "#" lines.
2567 SARIF requires lines > 0, so if we hit this case we don't have a
2568 way of validly representing the region as SARIF; bail out. */
2569 if (exploc_start
.line
<= 0)
2572 auto region_obj
= ::make_unique
<sarif_region
> ();
2574 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
2575 region_obj
->set_integer ("startLine", exploc_start
.line
);
2577 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
2578 if (exploc_finish
.line
!= exploc_start
.line
2579 && exploc_finish
.line
> 0)
2580 region_obj
->set_integer ("endLine", exploc_finish
.line
);
2582 /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
2583 if (auto artifact_content_obj
2584 = maybe_make_artifact_content_object (exploc_start
.file
,
2588 region_obj
->set
<sarif_artifact_content
> ("snippet",
2589 std::move (artifact_content_obj
));
2594 /* Make a "region" object (SARIF v2.1.0 section 3.30) for the deletion region
2595 of HINT (as per SARIF v2.1.0 section 3.57.3). */
2597 std::unique_ptr
<sarif_region
>
2598 sarif_builder::make_region_object_for_hint (const fixit_hint
&hint
) const
2600 location_t start_loc
= hint
.get_start_loc ();
2601 location_t next_loc
= hint
.get_next_loc ();
2603 expanded_location exploc_start
= expand_location (start_loc
);
2604 expanded_location exploc_next
= expand_location (next_loc
);
2606 auto region_obj
= ::make_unique
<sarif_region
> ();
2608 /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
2609 region_obj
->set_integer ("startLine", exploc_start
.line
);
2611 /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
2612 int start_col
= get_sarif_column (exploc_start
);
2613 region_obj
->set_integer ("startColumn", start_col
);
2615 /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
2616 if (exploc_next
.line
!= exploc_start
.line
)
2617 region_obj
->set_integer ("endLine", exploc_next
.line
);
2619 /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
2620 This expresses the column immediately beyond the range. */
2621 int next_col
= get_sarif_column (exploc_next
);
2622 region_obj
->set_integer ("endColumn", next_col
);
2627 /* Attempt to get a string for a logicalLocation's "kind" property
2628 (SARIF v2.1.0 section 3.33.7).
2629 Return nullptr if unknown. */
2632 maybe_get_sarif_kind (enum logical_location_kind kind
)
2638 case LOGICAL_LOCATION_KIND_UNKNOWN
:
2641 case LOGICAL_LOCATION_KIND_FUNCTION
:
2643 case LOGICAL_LOCATION_KIND_MEMBER
:
2645 case LOGICAL_LOCATION_KIND_MODULE
:
2647 case LOGICAL_LOCATION_KIND_NAMESPACE
:
2649 case LOGICAL_LOCATION_KIND_TYPE
:
2651 case LOGICAL_LOCATION_KIND_RETURN_TYPE
:
2652 return "returnType";
2653 case LOGICAL_LOCATION_KIND_PARAMETER
:
2655 case LOGICAL_LOCATION_KIND_VARIABLE
:
2660 /* Make a "logicalLocation" object (SARIF v2.1.0 section 3.33) for LOGICAL_LOC,
2661 or return nullptr. */
2663 std::unique_ptr
<sarif_logical_location
>
2664 make_sarif_logical_location_object (const logical_location
&logical_loc
)
2666 auto logical_loc_obj
= ::make_unique
<sarif_logical_location
> ();
2668 /* "name" property (SARIF v2.1.0 section 3.33.4). */
2669 if (const char *short_name
= logical_loc
.get_short_name ())
2670 logical_loc_obj
->set_string ("name", short_name
);
2672 /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
2673 if (const char *name_with_scope
= logical_loc
.get_name_with_scope ())
2674 logical_loc_obj
->set_string ("fullyQualifiedName", name_with_scope
);
2676 /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
2677 if (const char *internal_name
= logical_loc
.get_internal_name ())
2678 logical_loc_obj
->set_string ("decoratedName", internal_name
);
2680 /* "kind" property (SARIF v2.1.0 section 3.33.7). */
2681 enum logical_location_kind kind
= logical_loc
.get_kind ();
2682 if (const char *sarif_kind_str
= maybe_get_sarif_kind (kind
))
2683 logical_loc_obj
->set_string ("kind", sarif_kind_str
);
2685 return logical_loc_obj
;
2689 make_sarif_url_for_event (const sarif_code_flow
*code_flow
,
2690 diagnostic_event_id_t event_id
)
2692 gcc_assert (event_id
.known_p ());
2695 return label_text ();
2697 const sarif_thread_flow_location
&tfl_obj
2698 = code_flow
->get_thread_flow_loc_obj (event_id
);
2699 const int location_idx
= tfl_obj
.get_index_within_parent ();
2701 const sarif_thread_flow
&thread_flow_obj
= tfl_obj
.get_parent ();
2702 const int thread_flow_idx
= thread_flow_obj
.get_index_within_parent ();
2704 const sarif_code_flow
&code_flow_obj
= thread_flow_obj
.get_parent ();
2705 const int code_flow_idx
= code_flow_obj
.get_index_within_parent ();
2707 const sarif_result
&result_obj
= code_flow_obj
.get_parent ();
2708 const int result_idx
= result_obj
.get_index_within_parent ();
2710 /* We only support a single run object in the log. */
2711 const int run_idx
= 0;
2713 char *buf
= xasprintf
2714 ("sarif:/runs/%i/results/%i/codeFlows/%i/threadFlows/%i/locations/%i",
2715 run_idx
, result_idx
, code_flow_idx
, thread_flow_idx
, location_idx
);
2716 return label_text::take (buf
);
2719 /* Make a "codeFlow" object (SARIF v2.1.0 section 3.36) for PATH. */
2721 std::unique_ptr
<sarif_code_flow
>
2722 sarif_builder::make_code_flow_object (sarif_result
&result
,
2723 unsigned idx_within_parent
,
2724 const diagnostic_path
&path
)
2727 = ::make_unique
<sarif_code_flow
> (result
, idx_within_parent
);
2730 Create threadFlows and threadFlowLocation objects within them,
2731 effectively recording a mapping from event_id to threadFlowLocation
2732 so that we can later go from an event_id to a URI within the
2734 for (unsigned i
= 0; i
< path
.num_events (); i
++)
2736 const diagnostic_event
&event
= path
.get_event (i
);
2737 const diagnostic_thread_id_t thread_id
= event
.get_thread_id ();
2739 sarif_thread_flow
&thread_flow_obj
2740 = code_flow_obj
->get_or_append_thread_flow (path
.get_thread (thread_id
),
2742 thread_flow_obj
.add_location ();
2745 /* Second pass: walk the events, populating the tfl objs. */
2746 m_current_code_flow
= code_flow_obj
.get ();
2747 for (unsigned i
= 0; i
< path
.num_events (); i
++)
2749 const diagnostic_event
&event
= path
.get_event (i
);
2750 sarif_thread_flow_location
&thread_flow_loc_obj
2751 = code_flow_obj
->get_thread_flow_loc_obj (i
);
2752 populate_thread_flow_location_object (result
,
2753 thread_flow_loc_obj
,
2757 m_current_code_flow
= nullptr;
2759 return code_flow_obj
;
2762 /* Populate TFL_OBJ, a "threadFlowLocation" object (SARIF v2.1.0 section 3.38)
2767 populate_thread_flow_location_object (sarif_result
&result
,
2768 sarif_thread_flow_location
&tfl_obj
,
2769 const diagnostic_event
&ev
,
2770 int event_execution_idx
)
2772 /* Give diagnostic_event subclasses a chance to add custom properties
2773 via a property bag. */
2774 ev
.maybe_add_sarif_properties (tfl_obj
);
2776 /* "location" property (SARIF v2.1.0 section 3.38.3). */
2777 tfl_obj
.set
<sarif_location
>
2779 make_location_object (result
, ev
, diagnostic_artifact_role::traced_file
));
2781 /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
2782 diagnostic_event::meaning m
= ev
.get_meaning ();
2783 if (auto kinds_arr
= maybe_make_kinds_array (m
))
2784 tfl_obj
.set
<json::array
> ("kinds", std::move (kinds_arr
));
2786 /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
2787 tfl_obj
.set_integer ("nestingLevel", ev
.get_stack_depth ());
2789 /* "executionOrder" property (SARIF v2.1.0 3.38.11).
2790 Offset by 1 to match the human-readable values emitted by %@. */
2791 tfl_obj
.set_integer ("executionOrder", event_execution_idx
+ 1);
2793 /* It might be nice to eventually implement the following for -fanalyzer:
2794 - the "stack" property (SARIF v2.1.0 section 3.38.5)
2795 - the "state" property (SARIF v2.1.0 section 3.38.9)
2796 - the "importance" property (SARIF v2.1.0 section 3.38.13). */
2799 /* If M has any known meaning, make a json array suitable for the "kinds"
2800 property of a "threadFlowLocation" object (SARIF v2.1.0 section 3.38.8).
2802 Otherwise, return nullptr. */
2804 std::unique_ptr
<json::array
>
2805 sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m
) const
2807 if (m
.m_verb
== diagnostic_event::VERB_unknown
2808 && m
.m_noun
== diagnostic_event::NOUN_unknown
2809 && m
.m_property
== diagnostic_event::PROPERTY_unknown
)
2812 auto kinds_arr
= ::make_unique
<json::array
> ();
2813 if (const char *verb_str
2814 = diagnostic_event::meaning::maybe_get_verb_str (m
.m_verb
))
2815 kinds_arr
->append_string (verb_str
);
2816 if (const char *noun_str
2817 = diagnostic_event::meaning::maybe_get_noun_str (m
.m_noun
))
2818 kinds_arr
->append_string (noun_str
);
2819 if (const char *property_str
2820 = diagnostic_event::meaning::maybe_get_property_str (m
.m_property
))
2821 kinds_arr
->append_string (property_str
);
2825 /* In "3.11.5 Messages with placeholders":
2826 "Within both plain text and formatted message strings, the characters
2827 "{" and "}" SHALL be represented by the character sequences
2828 "{{" and "}}" respectively." */
2831 escape_braces (const char *text
)
2834 while (char ch
= *text
++)
2849 set_string_property_escaping_braces (json::object
&obj
,
2850 const char *property_name
,
2853 std::string
escaped (escape_braces (value
));
2854 obj
.set_string (property_name
, escaped
.c_str ());
2857 /* Make a "message" object (SARIF v2.1.0 section 3.11) for MSG. */
2859 std::unique_ptr
<sarif_message
>
2860 sarif_builder::make_message_object (const char *msg
) const
2862 auto message_obj
= ::make_unique
<sarif_message
> ();
2864 /* "text" property (SARIF v2.1.0 section 3.11.8). */
2865 set_string_property_escaping_braces (*message_obj
,
2871 /* Make a "message" object (SARIF v2.1.0 section 3.11) for DIAGRAM.
2872 We emit the diagram as a code block within the Markdown part
2875 std::unique_ptr
<sarif_message
>
2876 sarif_builder::make_message_object_for_diagram (const diagnostic_diagram
&diagram
)
2878 auto message_obj
= ::make_unique
<sarif_message
> ();
2880 /* "text" property (SARIF v2.1.0 section 3.11.8). */
2881 set_string_property_escaping_braces (*message_obj
,
2882 "text", diagram
.get_alt_text ());
2884 pretty_printer
*const pp
= m_printer
;
2885 char *saved_prefix
= pp_take_prefix (pp
);
2886 pp_set_prefix (pp
, nullptr);
2888 /* "To produce a code block in Markdown, simply indent every line of
2889 the block by at least 4 spaces or 1 tab."
2890 Here we use 4 spaces. */
2891 diagram
.get_canvas ().print_to_pp (pp
, " ");
2892 pp_set_prefix (pp
, saved_prefix
);
2894 /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
2895 set_string_property_escaping_braces (*message_obj
,
2896 "markdown", pp_formatted_text (pp
));
2898 pp_clear_output_area (pp
);
2903 /* Make a "multiformatMessageString object" (SARIF v2.1.0 section 3.12)
2906 std::unique_ptr
<sarif_multiformat_message_string
>
2907 sarif_builder::make_multiformat_message_string (const char *msg
) const
2909 auto message_obj
= ::make_unique
<sarif_multiformat_message_string
> ();
2911 /* "text" property (SARIF v2.1.0 section 3.12.3). */
2912 set_string_property_escaping_braces (*message_obj
,
2918 /* Convert VERSION to a value for the "$schema" property
2919 of a "sarifLog" object (SARIF v2.1.0 section 3.13.3). */
2922 sarif_version_to_url (enum sarif_version version
)
2928 case sarif_version::v2_1_0
:
2929 return "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json";
2930 case sarif_version::v2_2_prerelease_2024_08_08
:
2931 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";
2935 /* Convert VERSION to a value for the "version" property
2936 of a "sarifLog" object (SARIF v2.1.0 section 3.13.2). */
2939 sarif_version_to_property (enum sarif_version version
)
2945 case sarif_version::v2_1_0
:
2947 case sarif_version::v2_2_prerelease_2024_08_08
:
2948 /* I would have used "2.2-prerelease-2024-08-08",
2949 but the schema only accepts "2.2". */
2954 /* Make a top-level "sarifLog" object (SARIF v2.1.0 section 3.13). */
2956 std::unique_ptr
<sarif_log
>
2958 make_top_level_object (std::unique_ptr
<sarif_invocation
> invocation_obj
,
2959 std::unique_ptr
<json::array
> results
)
2961 auto log_obj
= ::make_unique
<sarif_log
> ();
2963 /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
2964 log_obj
->set_string ("$schema", sarif_version_to_url (m_version
));
2966 /* "version" property (SARIF v2.1.0 section 3.13.2). */
2967 log_obj
->set_string ("version", sarif_version_to_property (m_version
));
2969 /* "runs" property (SARIF v2.1.0 section 3.13.4). */
2970 auto run_arr
= ::make_unique
<json::array
> ();
2971 auto run_obj
= make_run_object (std::move (invocation_obj
),
2972 std::move (results
));
2973 run_arr
->append
<sarif_run
> (std::move (run_obj
));
2974 log_obj
->set
<json::array
> ("runs", std::move (run_arr
));
2979 /* Make a "run" object (SARIF v2.1.0 section 3.14). */
2981 std::unique_ptr
<sarif_run
>
2983 make_run_object (std::unique_ptr
<sarif_invocation
> invocation_obj
,
2984 std::unique_ptr
<json::array
> results
)
2986 auto run_obj
= ::make_unique
<sarif_run
> ();
2988 /* "tool" property (SARIF v2.1.0 section 3.14.6). */
2989 run_obj
->set
<sarif_tool
> ("tool", make_tool_object ());
2991 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
2992 if (auto taxonomies_arr
= maybe_make_taxonomies_array ())
2993 run_obj
->set
<json::array
> ("taxonomies", std::move (taxonomies_arr
));
2995 /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
2997 auto invocations_arr
= ::make_unique
<json::array
> ();
2998 invocations_arr
->append (std::move (invocation_obj
));
2999 run_obj
->set
<json::array
> ("invocations", std::move (invocations_arr
));
3002 /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
3003 if (m_seen_any_relative_paths
)
3005 auto orig_uri_base_ids
= ::make_unique
<json::object
> ();
3006 orig_uri_base_ids
->set
<sarif_artifact_location
>
3007 (PWD_PROPERTY_NAME
, make_artifact_location_object_for_pwd ());
3008 run_obj
->set
<json::object
> ("originalUriBaseIds",
3009 std::move (orig_uri_base_ids
));
3012 /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
3013 auto artifacts_arr
= ::make_unique
<json::array
> ();
3014 for (auto iter
: m_filename_to_artifact_map
)
3016 sarif_artifact
*artifact_obj
= iter
.second
;
3017 if (artifact_obj
->embed_contents_p ())
3018 artifact_obj
->populate_contents (*this);
3019 artifact_obj
->populate_roles ();
3020 artifacts_arr
->append (artifact_obj
);
3022 run_obj
->set
<json::array
> ("artifacts", std::move (artifacts_arr
));
3023 m_filename_to_artifact_map
.empty ();
3025 /* "results" property (SARIF v2.1.0 section 3.14.23). */
3026 run_obj
->set
<json::array
> ("results", std::move (results
));
3031 /* Make a "tool" object (SARIF v2.1.0 section 3.18). */
3033 std::unique_ptr
<sarif_tool
>
3034 sarif_builder::make_tool_object ()
3036 auto tool_obj
= ::make_unique
<sarif_tool
> ();
3038 /* "driver" property (SARIF v2.1.0 section 3.18.2). */
3039 tool_obj
->set
<sarif_tool_component
> ("driver",
3040 make_driver_tool_component_object ());
3042 /* Report plugins via the "extensions" property
3043 (SARIF v2.1.0 section 3.18.3). */
3044 if (auto client_data_hooks
= m_context
.get_client_data_hooks ())
3045 if (const client_version_info
*vinfo
3046 = client_data_hooks
->get_any_version_info ())
3048 class my_plugin_visitor
: public client_version_info :: plugin_visitor
3051 void on_plugin (const diagnostic_client_plugin_info
&p
) final override
3053 /* Create a "toolComponent" object (SARIF v2.1.0 section 3.19)
3055 auto plugin_obj
= ::make_unique
<sarif_tool_component
> ();
3057 /* "name" property (SARIF v2.1.0 section 3.19.8). */
3058 if (const char *short_name
= p
.get_short_name ())
3059 plugin_obj
->set_string ("name", short_name
);
3061 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
3062 if (const char *full_name
= p
.get_full_name ())
3063 plugin_obj
->set_string ("fullName", full_name
);
3065 /* "version" property (SARIF v2.1.0 section 3.19.13). */
3066 if (const char *version
= p
.get_version ())
3067 plugin_obj
->set_string ("version", version
);
3069 m_plugin_objs
.push_back (std::move (plugin_obj
));
3071 std::vector
<std::unique_ptr
<sarif_tool_component
>> m_plugin_objs
;
3073 my_plugin_visitor v
;
3074 vinfo
->for_each_plugin (v
);
3075 if (v
.m_plugin_objs
.size () > 0)
3077 auto extensions_arr
= ::make_unique
<json::array
> ();
3078 for (auto &iter
: v
.m_plugin_objs
)
3079 extensions_arr
->append
<sarif_tool_component
> (std::move (iter
));
3080 tool_obj
->set
<json::array
> ("extensions",
3081 std::move (extensions_arr
));
3085 /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
3086 "extensions" (see toplev.cc: print_version). */
3091 /* Make a "toolComponent" object (SARIF v2.1.0 section 3.19) for what SARIF
3092 calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
3094 std::unique_ptr
<sarif_tool_component
>
3095 sarif_builder::make_driver_tool_component_object ()
3097 auto driver_obj
= ::make_unique
<sarif_tool_component
> ();
3099 if (auto client_data_hooks
= m_context
.get_client_data_hooks ())
3100 if (const client_version_info
*vinfo
3101 = client_data_hooks
->get_any_version_info ())
3103 /* "name" property (SARIF v2.1.0 section 3.19.8). */
3104 if (const char *name
= vinfo
->get_tool_name ())
3105 driver_obj
->set_string ("name", name
);
3107 /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
3108 if (char *full_name
= vinfo
->maybe_make_full_name ())
3110 driver_obj
->set_string ("fullName", full_name
);
3114 /* "version" property (SARIF v2.1.0 section 3.19.13). */
3115 if (const char *version
= vinfo
->get_version_string ())
3116 driver_obj
->set_string ("version", version
);
3118 /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
3119 if (char *version_url
= vinfo
->maybe_make_version_url ())
3121 driver_obj
->set_string ("informationUri", version_url
);
3126 /* "rules" property (SARIF v2.1.0 section 3.19.23). */
3127 driver_obj
->set
<json::array
> ("rules", std::move (m_rules_arr
));
3132 /* If we've seen any CWE IDs, make an array for the "taxonomies" property
3133 (SARIF v2.1.0 section 3.14.8) of a run object, containing a single
3134 "toolComponent" (3.19) as per 3.19.3, representing the CWE.
3136 Otherwise return nullptr. */
3138 std::unique_ptr
<json::array
>
3139 sarif_builder::maybe_make_taxonomies_array () const
3141 auto cwe_obj
= maybe_make_cwe_taxonomy_object ();
3145 /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
3146 auto taxonomies_arr
= ::make_unique
<json::array
> ();
3147 taxonomies_arr
->append
<sarif_tool_component
> (std::move (cwe_obj
));
3148 return taxonomies_arr
;
3151 /* If we've seen any CWE IDs, make a "toolComponent" object
3152 (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
3153 Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
3155 Otherwise return nullptr. */
3157 std::unique_ptr
<sarif_tool_component
>
3158 sarif_builder::maybe_make_cwe_taxonomy_object () const
3160 if (m_cwe_id_set
.is_empty ())
3163 auto taxonomy_obj
= ::make_unique
<sarif_tool_component
> ();
3165 /* "name" property (SARIF v2.1.0 section 3.19.8). */
3166 taxonomy_obj
->set_string ("name", "CWE");
3168 /* "version" property (SARIF v2.1.0 section 3.19.13). */
3169 taxonomy_obj
->set_string ("version", "4.7");
3171 /* "organization" property (SARIF v2.1.0 section 3.19.18). */
3172 taxonomy_obj
->set_string ("organization", "MITRE");
3174 /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
3175 taxonomy_obj
->set
<sarif_multiformat_message_string
>
3176 ("shortDescription",
3177 make_multiformat_message_string ("The MITRE"
3178 " Common Weakness Enumeration"));
3180 /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
3181 auto taxa_arr
= ::make_unique
<json::array
> ();
3182 for (auto cwe_id
: m_cwe_id_set
)
3183 taxa_arr
->append
<sarif_reporting_descriptor
>
3184 (make_reporting_descriptor_object_for_cwe_id (cwe_id
));
3185 taxonomy_obj
->set
<json::array
> ("taxa", std::move (taxa_arr
));
3187 return taxonomy_obj
;
3190 /* Ensure that we have an "artifact" object (SARIF v2.1.0 section 3.24)
3191 for FILENAME, adding it to m_filename_to_artifact_map if not already
3192 found, and adding ROLE to it.
3193 If EMBED_CONTENTS is true, then flag that we will attempt to embed the
3194 contents of this artifact when writing it out. */
3197 sarif_builder::get_or_create_artifact (const char *filename
,
3198 enum diagnostic_artifact_role role
,
3199 bool embed_contents
)
3201 if (auto *slot
= m_filename_to_artifact_map
.get (filename
))
3203 (*slot
)->add_role (role
, embed_contents
);
3207 sarif_artifact
*artifact_obj
= new sarif_artifact (filename
);
3208 artifact_obj
->add_role (role
, embed_contents
);
3209 m_filename_to_artifact_map
.put (filename
, artifact_obj
);
3211 /* "location" property (SARIF v2.1.0 section 3.24.2). */
3212 artifact_obj
->set
<sarif_artifact_location
>
3213 ("location", make_artifact_location_object (filename
));
3215 /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
3220 case diagnostic_artifact_role::analysis_target
:
3221 case diagnostic_artifact_role::result_file
:
3222 case diagnostic_artifact_role::scanned_file
:
3223 case diagnostic_artifact_role::traced_file
:
3224 /* Assume that these are in the source language. */
3225 if (auto client_data_hooks
= m_context
.get_client_data_hooks ())
3226 if (const char *source_lang
3227 = client_data_hooks
->maybe_get_sarif_source_language (filename
))
3228 artifact_obj
->set_string ("sourceLanguage", source_lang
);
3231 case diagnostic_artifact_role::debug_output_file
:
3232 /* Assume that these are not in the source language. */
3236 return *artifact_obj
;
3239 /* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for the
3240 full contents of FILENAME. */
3242 std::unique_ptr
<sarif_artifact_content
>
3243 sarif_builder::maybe_make_artifact_content_object (const char *filename
) const
3245 /* Let input.cc handle any charset conversion. */
3246 char_span utf8_content
3247 = m_context
.get_file_cache ().get_source_file_content (filename
);
3251 /* Don't add it if it's not valid UTF-8. */
3252 if (!cpp_valid_utf8_p(utf8_content
.get_buffer (), utf8_content
.length ()))
3255 auto artifact_content_obj
= ::make_unique
<sarif_artifact_content
> ();
3256 artifact_content_obj
->set
<json::string
>
3258 ::make_unique
<json::string
> (utf8_content
.get_buffer (),
3259 utf8_content
.length ()));
3260 return artifact_content_obj
;
3263 /* Attempt to read the given range of lines from FILENAME; return
3264 a freshly-allocated 0-terminated buffer containing them, or nullptr. */
3267 sarif_builder::get_source_lines (const char *filename
,
3271 auto_vec
<char> result
;
3273 for (int line
= start_line
; line
<= end_line
; line
++)
3275 char_span line_content
3276 = m_context
.get_file_cache ().get_source_line (filename
, line
);
3277 if (!line_content
.get_buffer ())
3279 result
.reserve (line_content
.length () + 1);
3280 for (size_t i
= 0; i
< line_content
.length (); i
++)
3281 result
.quick_push (line_content
[i
]);
3282 result
.quick_push ('\n');
3284 result
.safe_push ('\0');
3286 return xstrdup (result
.address ());
3289 /* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for the given
3290 run of lines within FILENAME (including the endpoints).
3291 If R is non-NULL, use it to potentially set the "rendered"
3292 property (3.3.4). */
3294 std::unique_ptr
<sarif_artifact_content
>
3296 maybe_make_artifact_content_object (const char *filename
,
3299 const content_renderer
*r
) const
3301 char *text_utf8
= get_source_lines (filename
, start_line
, end_line
);
3306 /* Don't add it if it's not valid UTF-8. */
3307 if (!cpp_valid_utf8_p(text_utf8
, strlen(text_utf8
)))
3313 auto artifact_content_obj
= ::make_unique
<sarif_artifact_content
> ();
3314 artifact_content_obj
->set_string ("text", text_utf8
);
3317 /* 3.3.4 "rendered" property. */
3319 if (std::unique_ptr
<sarif_multiformat_message_string
> rendered
3320 = r
->render (*this))
3321 artifact_content_obj
->set ("rendered", std::move (rendered
));
3323 return artifact_content_obj
;
3326 /* Make a "fix" object (SARIF v2.1.0 section 3.55) for RICHLOC. */
3328 std::unique_ptr
<sarif_fix
>
3329 sarif_builder::make_fix_object (const rich_location
&richloc
)
3331 auto fix_obj
= ::make_unique
<sarif_fix
> ();
3333 /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
3334 /* We assume that all fix-it hints in RICHLOC affect the same file. */
3335 auto artifact_change_arr
= ::make_unique
<json::array
> ();
3336 artifact_change_arr
->append
<sarif_artifact_change
>
3337 (make_artifact_change_object (richloc
));
3338 fix_obj
->set
<json::array
> ("artifactChanges",
3339 std::move (artifact_change_arr
));
3344 /* Make an "artifactChange" object (SARIF v2.1.0 section 3.56) for RICHLOC. */
3346 std::unique_ptr
<sarif_artifact_change
>
3347 sarif_builder::make_artifact_change_object (const rich_location
&richloc
)
3349 auto artifact_change_obj
= ::make_unique
<sarif_artifact_change
> ();
3351 /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
3352 artifact_change_obj
->set
<sarif_artifact_location
>
3353 ("artifactLocation",
3354 make_artifact_location_object (richloc
.get_loc ()));
3356 /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
3357 auto replacement_arr
= ::make_unique
<json::array
> ();
3358 for (unsigned int i
= 0; i
< richloc
.get_num_fixit_hints (); i
++)
3360 const fixit_hint
*hint
= richloc
.get_fixit_hint (i
);
3361 replacement_arr
->append
<sarif_replacement
>
3362 (make_replacement_object (*hint
));
3364 artifact_change_obj
->set
<json::array
> ("replacements",
3365 std::move (replacement_arr
));
3367 return artifact_change_obj
;
3370 /* Make a "replacement" object (SARIF v2.1.0 section 3.57) for HINT. */
3372 std::unique_ptr
<sarif_replacement
>
3373 sarif_builder::make_replacement_object (const fixit_hint
&hint
) const
3375 auto replacement_obj
= ::make_unique
<sarif_replacement
> ();
3377 /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
3378 replacement_obj
->set
<sarif_region
> ("deletedRegion",
3379 make_region_object_for_hint (hint
));
3381 /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
3382 replacement_obj
->set
<sarif_artifact_content
>
3384 make_artifact_content_object (hint
.get_string ()));
3386 return replacement_obj
;
3389 /* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for TEXT. */
3391 std::unique_ptr
<sarif_artifact_content
>
3392 sarif_builder::make_artifact_content_object (const char *text
) const
3394 auto content_obj
= ::make_unique
<sarif_artifact_content
> ();
3396 /* "text" property (SARIF v2.1.0 section 3.3.2). */
3397 content_obj
->set_string ("text", text
);
3402 /* class diagnostic_sarif_format_buffer : public diagnostic_per_format_buffer. */
3405 diagnostic_sarif_format_buffer::dump (FILE *out
, int indent
) const
3407 fprintf (out
, "%*sdiagnostic_sarif_format_buffer:\n", indent
, "");
3409 for (auto &result
: m_results
)
3411 fprintf (out
, "%*sresult[%i]:\n", indent
+ 2, "", idx
);
3412 result
->dump (out
, true);
3413 fprintf (out
, "\n");
3419 diagnostic_sarif_format_buffer::empty_p () const
3421 return m_results
.empty ();
3425 diagnostic_sarif_format_buffer::move_to (diagnostic_per_format_buffer
&base
)
3427 diagnostic_sarif_format_buffer
&dest
3428 = static_cast<diagnostic_sarif_format_buffer
&> (base
);
3429 for (auto &&result
: m_results
)
3430 dest
.m_results
.push_back (std::move (result
));
3435 diagnostic_sarif_format_buffer::clear ()
3441 diagnostic_sarif_format_buffer::flush ()
3443 for (auto &&result
: m_results
)
3445 result
->process_worklist (m_builder
);
3446 m_builder
.m_results_array
->append
<sarif_result
> (std::move (result
));
3451 class sarif_output_format
: public diagnostic_output_format
3454 ~sarif_output_format ()
3456 /* Any sarifResult objects should have been handled by now.
3457 If not, then something's gone wrong with diagnostic
3459 std::unique_ptr
<sarif_result
> pending_result
3460 = m_builder
.take_current_result ();
3461 gcc_assert (!pending_result
);
3464 void dump (FILE *out
, int indent
) const override
3466 fprintf (out
, "%*ssarif_output_format\n", indent
, "");
3467 diagnostic_output_format::dump (out
, indent
);
3470 std::unique_ptr
<diagnostic_per_format_buffer
>
3471 make_per_format_buffer () final override
3473 return ::make_unique
<diagnostic_sarif_format_buffer
> (m_builder
);
3475 void set_buffer (diagnostic_per_format_buffer
*base_buffer
) final override
3477 diagnostic_sarif_format_buffer
*buffer
3478 = static_cast<diagnostic_sarif_format_buffer
*> (base_buffer
);
3482 bool follows_reference_printer_p () const final override
3487 void update_printer () final override
3489 m_printer
= m_context
.clone_printer ();
3491 /* Don't colorize the text. */
3492 pp_show_color (m_printer
.get ()) = false;
3494 /* No textual URLs. */
3495 m_printer
->set_url_format (URL_FORMAT_NONE
);
3497 /* Use builder's token printer. */
3498 get_printer ()->set_token_printer (&m_builder
.get_token_printer ());
3500 /* Update the builder to use the new printer. */
3501 m_builder
.set_printer (*get_printer ());
3504 void on_begin_group () final override
3508 void on_end_group () final override
3510 m_builder
.end_group ();
3513 on_report_diagnostic (const diagnostic_info
&diagnostic
,
3514 diagnostic_t orig_diag_kind
) final override
3516 m_builder
.on_report_diagnostic (diagnostic
, orig_diag_kind
, m_buffer
);
3518 void on_diagram (const diagnostic_diagram
&diagram
) final override
3520 m_builder
.emit_diagram (diagram
);
3522 void after_diagnostic (const diagnostic_info
&) final override
3527 sarif_builder
&get_builder () { return m_builder
; }
3529 size_t num_results () const { return m_builder
.num_results (); }
3530 sarif_result
&get_result (size_t idx
) { return m_builder
.get_result (idx
); }
3533 sarif_output_format (diagnostic_context
&context
,
3534 const line_maps
*line_maps
,
3535 const char *main_input_filename_
,
3537 enum sarif_version version
)
3538 : diagnostic_output_format (context
),
3539 m_builder (context
, *get_printer (), line_maps
, main_input_filename_
,
3540 formatted
, version
),
3544 sarif_builder m_builder
;
3545 diagnostic_sarif_format_buffer
*m_buffer
;
3548 class sarif_stream_output_format
: public sarif_output_format
3551 sarif_stream_output_format (diagnostic_context
&context
,
3552 const line_maps
*line_maps
,
3553 const char *main_input_filename_
,
3555 enum sarif_version version
,
3557 : sarif_output_format (context
, line_maps
, main_input_filename_
,
3558 formatted
, version
),
3562 ~sarif_stream_output_format ()
3564 m_builder
.flush_to_file (m_stream
);
3566 bool machine_readable_stderr_p () const final override
3568 return m_stream
== stderr
;
3574 class sarif_file_output_format
: public sarif_output_format
3577 sarif_file_output_format (diagnostic_context
&context
,
3578 const line_maps
*line_maps
,
3579 const char *main_input_filename_
,
3581 enum sarif_version version
,
3582 diagnostic_output_file output_file
)
3583 : sarif_output_format (context
, line_maps
, main_input_filename_
,
3584 formatted
, version
),
3585 m_output_file (std::move (output_file
))
3587 gcc_assert (m_output_file
.get_open_file ());
3588 gcc_assert (m_output_file
.get_filename ());
3590 ~sarif_file_output_format ()
3592 m_builder
.flush_to_file (m_output_file
.get_open_file ());
3594 void dump (FILE *out
, int indent
) const override
3596 fprintf (out
, "%*ssarif_file_output_format: %s\n",
3598 m_output_file
.get_filename ());
3599 diagnostic_output_format::dump (out
, indent
);
3601 bool machine_readable_stderr_p () const final override
3607 diagnostic_output_file m_output_file
;
3610 /* Print the start of an embedded link to PP, as per 3.11.6. */
3613 sarif_begin_embedded_link (pretty_printer
*pp
)
3615 pp_character (pp
, '[');
3618 /* Print the end of an embedded link to PP, as per 3.11.6. */
3621 sarif_end_embedded_link (pretty_printer
*pp
,
3624 pp_string (pp
, "](");
3625 /* TODO: does the URI need escaping?
3626 See https://github.com/oasis-tcs/sarif-spec/issues/657 */
3627 pp_string (pp
, url
);
3628 pp_character (pp
, ')');
3631 /* class sarif_token_printer : public token_printer. */
3633 /* Implementation of pretty_printer::token_printer for SARIF output.
3634 Emit URLs as per 3.11.6 ("Messages with embedded links"). */
3637 sarif_builder::sarif_token_printer::print_tokens (pretty_printer
*pp
,
3638 const pp_token_list
&tokens
)
3640 /* Convert to text, possibly with colorization, URLs, etc. */
3641 label_text current_url
;
3642 for (auto iter
= tokens
.m_first
; iter
; iter
= iter
->m_next
)
3643 switch (iter
->m_kind
)
3648 case pp_token::kind::text
:
3650 const pp_token_text
*sub
= as_a
<const pp_token_text
*> (iter
);
3651 const char * const str
= sub
->m_value
.get ();
3652 if (current_url
.get ())
3654 /* Write iter->m_value, but escaping any
3655 escaped link characters as per 3.11.6. */
3656 for (const char *ptr
= str
; *ptr
; ptr
++)
3658 const char ch
= *ptr
;
3662 pp_character (pp
, ch
);
3667 pp_character (pp
, '\\');
3668 pp_character (pp
, ch
);
3674 /* TODO: is other escaping needed? (e.g. of '[')
3675 See https://github.com/oasis-tcs/sarif-spec/issues/658 */
3676 pp_string (pp
, str
);
3680 case pp_token::kind::begin_color
:
3681 case pp_token::kind::end_color
:
3682 /* These are no-ops. */
3685 case pp_token::kind::begin_quote
:
3686 pp_begin_quote (pp
, pp_show_color (pp
));
3688 case pp_token::kind::end_quote
:
3689 pp_end_quote (pp
, pp_show_color (pp
));
3692 /* Emit URLs as per 3.11.6 ("Messages with embedded links"). */
3693 case pp_token::kind::begin_url
:
3695 pp_token_begin_url
*sub
= as_a
<pp_token_begin_url
*> (iter
);
3696 sarif_begin_embedded_link (pp
);
3697 current_url
= std::move (sub
->m_value
);
3700 case pp_token::kind::end_url
:
3701 gcc_assert (current_url
.get ());
3702 sarif_end_embedded_link (pp
, current_url
.get ());
3703 current_url
= label_text::borrow (nullptr);
3706 case pp_token::kind::event_id
:
3708 pp_token_event_id
*sub
= as_a
<pp_token_event_id
*> (iter
);
3709 gcc_assert (sub
->m_event_id
.known_p ());
3710 const sarif_code_flow
*code_flow
3711 = m_builder
.get_code_flow_for_event_ids ();
3712 label_text url
= make_sarif_url_for_event (code_flow
,
3715 sarif_begin_embedded_link (pp
);
3716 pp_character (pp
, '(');
3717 pp_decimal_int (pp
, sub
->m_event_id
.one_based ());
3718 pp_character (pp
, ')');
3720 sarif_end_embedded_link (pp
, url
.get ());
3726 /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
3730 diagnostic_output_format_init_sarif (diagnostic_context
&context
,
3731 std::unique_ptr
<sarif_output_format
> fmt
)
3733 fmt
->update_printer ();
3735 context
.set_output_format (std::move (fmt
));
3738 /* Populate CONTEXT in preparation for SARIF output to stderr. */
3741 diagnostic_output_format_init_sarif_stderr (diagnostic_context
&context
,
3742 const line_maps
*line_maps
,
3743 const char *main_input_filename_
,
3745 enum sarif_version version
)
3747 gcc_assert (line_maps
);
3748 diagnostic_output_format_init_sarif
3750 ::make_unique
<sarif_stream_output_format
> (context
,
3752 main_input_filename_
,
3758 /* Attempt to open BASE_FILE_NAME.sarif for writing.
3759 Return a non-null diagnostic_output_file,
3760 or return a null diagnostic_output_file and complain to CONTEXT
3763 diagnostic_output_file
3764 diagnostic_output_format_open_sarif_file (diagnostic_context
&context
,
3765 line_maps
*line_maps
,
3766 const char *base_file_name
)
3768 if (!base_file_name
)
3770 rich_location
richloc (line_maps
, UNKNOWN_LOCATION
);
3771 context
.emit_diagnostic_with_group
3772 (DK_ERROR
, richloc
, nullptr, 0,
3773 "unable to determine filename for SARIF output");
3774 return diagnostic_output_file ();
3777 label_text filename
= label_text::take (concat (base_file_name
,
3780 FILE *outf
= fopen (filename
.get (), "w");
3783 rich_location
richloc (line_maps
, UNKNOWN_LOCATION
);
3784 context
.emit_diagnostic_with_group
3785 (DK_ERROR
, richloc
, nullptr, 0,
3786 "unable to open %qs for SARIF output: %m",
3788 return diagnostic_output_file ();
3790 return diagnostic_output_file (outf
, true, std::move (filename
));
3793 /* Populate CONTEXT in preparation for SARIF output to a file named
3794 BASE_FILE_NAME.sarif. */
3797 diagnostic_output_format_init_sarif_file (diagnostic_context
&context
,
3798 line_maps
*line_maps
,
3799 const char *main_input_filename_
,
3801 enum sarif_version version
,
3802 const char *base_file_name
)
3804 gcc_assert (line_maps
);
3806 diagnostic_output_file output_file
3807 = diagnostic_output_format_open_sarif_file (context
,
3811 diagnostic_output_format_init_sarif
3813 ::make_unique
<sarif_file_output_format
> (context
,
3815 main_input_filename_
,
3818 std::move (output_file
)));
3821 /* Populate CONTEXT in preparation for SARIF output to STREAM. */
3824 diagnostic_output_format_init_sarif_stream (diagnostic_context
&context
,
3825 const line_maps
*line_maps
,
3826 const char *main_input_filename_
,
3828 enum sarif_version version
,
3831 gcc_assert (line_maps
);
3832 diagnostic_output_format_init_sarif
3834 ::make_unique
<sarif_stream_output_format
> (context
,
3836 main_input_filename_
,
3842 std::unique_ptr
<diagnostic_output_format
>
3843 make_sarif_sink (diagnostic_context
&context
,
3844 const line_maps
&line_maps
,
3845 const char *main_input_filename_
,
3846 enum sarif_version version
,
3847 diagnostic_output_file output_file
)
3849 auto sink
= ::make_unique
<sarif_file_output_format
> (context
,
3851 main_input_filename_
,
3854 std::move (output_file
));
3855 sink
->update_printer ();
3861 namespace selftest
{
3863 /* A subclass of sarif_output_format for writing selftests.
3864 The JSON output is cached internally, rather than written
3867 class test_sarif_diagnostic_context
: public test_diagnostic_context
3870 test_sarif_diagnostic_context (const char *main_input_filename
,
3871 enum sarif_version version
)
3873 auto format
= ::make_unique
<buffered_output_format
> (*this,
3875 main_input_filename
,
3878 m_format
= format
.get (); // borrowed
3879 diagnostic_output_format_init_sarif (*this, std::move (format
));
3882 std::unique_ptr
<sarif_log
> flush_to_object ()
3884 return m_format
->flush_to_object ();
3887 size_t num_results () const { return m_format
->num_results (); }
3888 sarif_result
&get_result (size_t idx
) { return m_format
->get_result (idx
); }
3891 class buffered_output_format
: public sarif_output_format
3894 buffered_output_format (diagnostic_context
&context
,
3895 const line_maps
*line_maps
,
3896 const char *main_input_filename_
,
3898 enum sarif_version version
)
3899 : sarif_output_format (context
, line_maps
, main_input_filename_
,
3903 bool machine_readable_stderr_p () const final override
3907 std::unique_ptr
<sarif_log
> flush_to_object ()
3909 return m_builder
.flush_to_object ();
3913 buffered_output_format
*m_format
; // borrowed
3916 /* Test making a sarif_location for a complex rich_location
3917 with labels and escape-on-output. */
3920 test_make_location_object (const line_table_case
&case_
,
3921 enum sarif_version version
)
3923 diagnostic_show_locus_fixture_one_liner_utf8
f (case_
);
3924 location_t line_end
= linemap_position_for_column (line_table
, 31);
3926 /* Don't attempt to run the tests if column data might be unavailable. */
3927 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
3930 test_diagnostic_context dc
;
3932 sarif_builder
builder (dc
, pp
, line_table
, "MAIN_INPUT_FILENAME",
3935 /* These "columns" are byte offsets, whereas later on the columns
3936 in the generated SARIF use sarif_builder::get_sarif_column and
3937 thus respect tabs, encoding. */
3938 const location_t foo
3939 = make_location (linemap_position_for_column (line_table
, 1),
3940 linemap_position_for_column (line_table
, 1),
3941 linemap_position_for_column (line_table
, 8));
3942 const location_t bar
3943 = make_location (linemap_position_for_column (line_table
, 12),
3944 linemap_position_for_column (line_table
, 12),
3945 linemap_position_for_column (line_table
, 17));
3946 const location_t field
3947 = make_location (linemap_position_for_column (line_table
, 19),
3948 linemap_position_for_column (line_table
, 19),
3949 linemap_position_for_column (line_table
, 30));
3951 text_range_label
label0 ("label0");
3952 text_range_label
label1 ("label1");
3953 text_range_label
label2 ("label2");
3955 rich_location
richloc (line_table
, foo
, &label0
, nullptr);
3956 richloc
.add_range (bar
, SHOW_RANGE_WITHOUT_CARET
, &label1
);
3957 richloc
.add_range (field
, SHOW_RANGE_WITHOUT_CARET
, &label2
);
3958 richloc
.set_escape_on_output (true);
3960 sarif_result
result (0);
3962 std::unique_ptr
<sarif_location
> location_obj
3963 = builder
.make_location_object
3964 (result
, richloc
, nullptr, diagnostic_artifact_role::analysis_target
);
3965 ASSERT_NE (location_obj
, nullptr);
3967 auto physical_location
3968 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (location_obj
.get (),
3969 "physicalLocation");
3972 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location
, "region");
3973 ASSERT_JSON_INT_PROPERTY_EQ (region
, "startLine", 1);
3974 ASSERT_JSON_INT_PROPERTY_EQ (region
, "startColumn", 1);
3975 ASSERT_JSON_INT_PROPERTY_EQ (region
, "endColumn", 7);
3979 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location
,
3981 ASSERT_JSON_INT_PROPERTY_EQ (context_region
, "startLine", 1);
3985 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (context_region
, "snippet");
3987 /* We expect the snippet's "text" to be a copy of the content. */
3988 ASSERT_JSON_STRING_PROPERTY_EQ (snippet
, "text", f
.m_content
);
3990 /* We expect the snippet to have a "rendered" whose "text" has a
3991 pure ASCII escaped copy of the line (with labels, etc). */
3994 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (snippet
, "rendered");
3995 ASSERT_JSON_STRING_PROPERTY_EQ
3997 "1 | <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
3998 " | ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4000 " | label0 label1 label2\n");
4005 = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (location_obj
.get (),
4007 ASSERT_EQ (annotations
->size (), 3);
4010 auto a0
= (*annotations
)[0];
4011 ASSERT_JSON_INT_PROPERTY_EQ (a0
, "startLine", 1);
4012 ASSERT_JSON_INT_PROPERTY_EQ (a0
, "startColumn", 1);
4013 ASSERT_JSON_INT_PROPERTY_EQ (a0
, "endColumn", 7);
4015 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (a0
, "message");
4016 ASSERT_JSON_STRING_PROPERTY_EQ (message
, "text", "label0");
4019 auto a1
= (*annotations
)[1];
4020 ASSERT_JSON_INT_PROPERTY_EQ (a1
, "startLine", 1);
4021 ASSERT_JSON_INT_PROPERTY_EQ (a1
, "startColumn", 10);
4022 ASSERT_JSON_INT_PROPERTY_EQ (a1
, "endColumn", 15);
4024 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (a1
, "message");
4025 ASSERT_JSON_STRING_PROPERTY_EQ (message
, "text", "label1");
4028 auto a2
= (*annotations
)[2];
4029 ASSERT_JSON_INT_PROPERTY_EQ (a2
, "startLine", 1);
4030 ASSERT_JSON_INT_PROPERTY_EQ (a2
, "startColumn", 16);
4031 ASSERT_JSON_INT_PROPERTY_EQ (a2
, "endColumn", 25);
4033 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (a2
, "message");
4034 ASSERT_JSON_STRING_PROPERTY_EQ (message
, "text", "label2");
4039 /* Test of reporting a diagnostic at UNKNOWN_LOCATION to a
4040 diagnostic_context and examining the generated sarif_log.
4041 Verify various basic properties. */
4044 test_simple_log (enum sarif_version version
)
4046 test_sarif_diagnostic_context
dc ("MAIN_INPUT_FILENAME", version
);
4048 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
4049 dc
.report (DK_ERROR
, richloc
, nullptr, 0, "this is a test: %i", 42);
4051 auto log_ptr
= dc
.flush_to_object ();
4054 auto log
= log_ptr
.get ();
4055 ASSERT_JSON_STRING_PROPERTY_EQ (log
, "$schema",
4056 sarif_version_to_url (version
));
4057 ASSERT_JSON_STRING_PROPERTY_EQ (log
, "version",
4058 sarif_version_to_property (version
));
4060 auto runs
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log
, "runs"); // 3.13.4
4061 ASSERT_EQ (runs
->size (), 1);
4063 // 3.14 "run" object:
4064 auto run
= (*runs
)[0];
4068 auto tool
= EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (run
, "tool");
4070 EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (tool
, "driver"); // 3.18.2
4076 = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run
, "invocations");
4077 ASSERT_EQ (invocations
->size (), 1);
4080 // 3.20 "invocation" object:
4081 auto invocation
= (*invocations
)[0];
4083 // 3.20.3 arguments property
4085 // 3.20.7 startTimeUtc property
4086 EXPECT_JSON_OBJECT_WITH_STRING_PROPERTY (invocation
, "startTimeUtc");
4088 // 3.20.8 endTimeUtc property
4089 EXPECT_JSON_OBJECT_WITH_STRING_PROPERTY (invocation
, "endTimeUtc");
4091 // 3.20.19 workingDirectory property
4094 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (invocation
,
4095 "workingDirectory");
4096 EXPECT_JSON_OBJECT_WITH_STRING_PROPERTY (wd_obj
, "uri");
4099 // 3.20.21 toolExecutionNotifications property
4101 = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY
4102 (invocation
, "toolExecutionNotifications");
4103 ASSERT_EQ (notifications
->size (), 0);
4109 auto artifacts
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run
, "artifacts");
4110 ASSERT_EQ (artifacts
->size (), 1);
4113 // 3.24 "artifact" object:
4114 auto artifact
= (*artifacts
)[0];
4118 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (artifact
, "location");
4119 ASSERT_JSON_STRING_PROPERTY_EQ (location
, "uri", "MAIN_INPUT_FILENAME");
4122 auto roles
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (artifact
, "roles");
4123 ASSERT_EQ (roles
->size (), 1);
4125 auto role
= (*roles
)[0];
4126 ASSERT_JSON_STRING_EQ (role
, "analysisTarget");
4133 auto results
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run
, "results");
4134 ASSERT_EQ (results
->size (), 1);
4137 // 3.27 "result" object:
4138 auto result
= (*results
)[0];
4139 ASSERT_JSON_STRING_PROPERTY_EQ (result
, "ruleId", "error");
4140 ASSERT_JSON_STRING_PROPERTY_EQ (result
, "level", "error"); // 3.27.10
4145 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result
, "message");
4146 ASSERT_JSON_STRING_PROPERTY_EQ (message
, "text",
4147 "this is a test: 42");
4152 = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (result
, "locations");
4153 ASSERT_EQ (locations
->size (), 0);
4158 /* As above, but with a "real" location_t. */
4161 test_simple_log_2 (const line_table_case
&case_
,
4162 enum sarif_version version
)
4164 auto_fix_quotes fix_quotes
;
4166 const char *const content
4169 = "unsinged int i;\n";
4170 diagnostic_show_locus_fixture
f (case_
, content
);
4171 location_t line_end
= linemap_position_for_column (line_table
, 31);
4173 /* Don't attempt to run the tests if column data might be unavailable. */
4174 if (line_end
> LINE_MAP_MAX_LOCATION_WITH_COLS
)
4177 test_sarif_diagnostic_context
dc (f
.get_filename (), version
);
4179 const location_t typo_loc
4180 = make_location (linemap_position_for_column (line_table
, 1),
4181 linemap_position_for_column (line_table
, 1),
4182 linemap_position_for_column (line_table
, 8));
4184 rich_location
richloc (line_table
, typo_loc
);
4185 dc
.report (DK_ERROR
, richloc
, nullptr, 0,
4186 "did you misspell %qs again?",
4189 auto log_ptr
= dc
.flush_to_object ();
4192 auto log
= log_ptr
.get ();
4194 auto runs
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log
, "runs"); // 3.13.4
4195 ASSERT_EQ (runs
->size (), 1);
4197 // 3.14 "run" object:
4198 auto run
= (*runs
)[0];
4202 auto results
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run
, "results");
4203 ASSERT_EQ (results
->size (), 1);
4206 // 3.27 "result" object:
4207 auto result
= (*results
)[0];
4208 ASSERT_JSON_STRING_PROPERTY_EQ (result
, "ruleId", "error");
4209 ASSERT_JSON_STRING_PROPERTY_EQ (result
, "level", "error"); // 3.27.10
4214 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result
, "message");
4215 ASSERT_JSON_STRING_PROPERTY_EQ (message
, "text",
4216 "did you misspell `unsigned' again?");
4221 = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (result
, "locations");
4222 ASSERT_EQ (locations
->size (), 1);
4225 // 3.28 "location" object:
4226 auto location
= (*locations
)[0];
4228 auto physical_location
4229 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (location
,
4230 "physicalLocation");
4233 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location
,
4235 ASSERT_JSON_INT_PROPERTY_EQ (region
, "startLine", 1);
4236 ASSERT_JSON_INT_PROPERTY_EQ (region
, "startColumn", 1);
4237 ASSERT_JSON_INT_PROPERTY_EQ (region
, "endColumn", 9);
4241 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location
,
4243 ASSERT_JSON_INT_PROPERTY_EQ (context_region
, "startLine", 1);
4247 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (context_region
,
4250 /* We expect the snippet's "text" to be a copy of the content. */
4251 ASSERT_JSON_STRING_PROPERTY_EQ (snippet
, "text", f
.m_content
);
4259 /* Assuming that a single diagnostic has been emitted within
4260 LOG, get a json::object for the result object. */
4262 static const json::object
*
4263 get_result_from_log (const sarif_log
*log
)
4265 auto runs
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log
, "runs"); // 3.13.4
4266 ASSERT_EQ (runs
->size (), 1);
4268 // 3.14 "run" object:
4269 auto run
= (*runs
)[0];
4272 auto results
= EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run
, "results");
4273 ASSERT_EQ (results
->size (), 1);
4275 // 3.27 "result" object:
4276 auto result
= (*results
)[0];
4277 return expect_json_object (SELFTEST_LOCATION
, result
);
4280 static const json::object
*
4281 get_message_from_result (const sarif_result
&result
)
4285 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (&result
, "message");
4289 /* Assuming that a single diagnostic has been emitted to
4290 DC, get a json::object for the messsage object within
4293 static const json::object
*
4294 get_message_from_log (const sarif_log
*log
)
4296 auto result_obj
= get_result_from_log (log
);
4300 = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result_obj
, "message");
4304 /* Tests of messages with embedded links; see SARIF v2.1.0 3.11.6. */
4307 test_message_with_embedded_link (enum sarif_version version
)
4309 auto_fix_quotes fix_quotes
;
4311 test_sarif_diagnostic_context
dc ("test.c", version
);
4312 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
4313 dc
.report (DK_ERROR
, richloc
, nullptr, 0,
4314 "before %{text%} after",
4315 "http://example.com");
4316 std::unique_ptr
<sarif_log
> log
= dc
.flush_to_object ();
4318 auto message_obj
= get_message_from_log (log
.get ());
4319 ASSERT_JSON_STRING_PROPERTY_EQ
4320 (message_obj
, "text",
4321 "before [text](http://example.com) after");
4324 /* Escaping in message text.
4325 This is "EXAMPLE 1" from 3.11.6. */
4327 test_sarif_diagnostic_context
dc ("test.c", version
);
4328 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
4330 /* Disable "unquoted sequence of 2 consecutive punctuation
4331 characters `]\' in format" warning. */
4333 # pragma GCC diagnostic push
4334 # pragma GCC diagnostic ignored "-Wformat-diag"
4336 dc
.report (DK_ERROR
, richloc
, nullptr, 0,
4337 "Prohibited term used in %{para[0]\\spans[2]%}.",
4340 # pragma GCC diagnostic pop
4343 std::unique_ptr
<sarif_log
> log
= dc
.flush_to_object ();
4345 auto message_obj
= get_message_from_log (log
.get ());
4346 ASSERT_JSON_STRING_PROPERTY_EQ
4347 (message_obj
, "text",
4348 "Prohibited term used in [para\\[0\\]\\\\spans\\[2\\]](1).");
4349 /* This isn't exactly what EXAMPLE 1 of the spec has; reported as
4350 https://github.com/oasis-tcs/sarif-spec/issues/656 */
4355 class test_urlifier
: public urlifier
4359 get_url_for_quoted_text (const char *p
, size_t sz
) const final override
4361 if (!strncmp (p
, "-foption", sz
))
4362 return xstrdup ("http://example.com");
4367 test_sarif_diagnostic_context
dc ("test.c", version
);
4368 dc
.set_urlifier (::make_unique
<test_urlifier
> ());
4369 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
4370 dc
.report (DK_ERROR
, richloc
, nullptr, 0,
4371 "foo %<-foption%> %<unrecognized%> bar");
4372 std::unique_ptr
<sarif_log
> log
= dc
.flush_to_object ();
4374 auto message_obj
= get_message_from_log (log
.get ());
4375 ASSERT_JSON_STRING_PROPERTY_EQ
4376 (message_obj
, "text",
4377 "foo `[-foption](http://example.com)' `unrecognized' bar");
4381 /* Verify that braces in messages get escaped, as per
4382 3.11.5 ("Messages with placeholders"). */
4385 test_message_with_braces (enum sarif_version version
)
4387 auto_fix_quotes fix_quotes
;
4389 test_sarif_diagnostic_context
dc ("test.c", version
);
4390 rich_location
richloc (line_table
, UNKNOWN_LOCATION
);
4391 dc
.report (DK_ERROR
, richloc
, nullptr, 0,
4392 "open brace: %qs close brace: %qs",
4394 std::unique_ptr
<sarif_log
> log
= dc
.flush_to_object ();
4396 auto message_obj
= get_message_from_log (log
.get ());
4397 ASSERT_JSON_STRING_PROPERTY_EQ
4398 (message_obj
, "text",
4399 "open brace: `{{' close brace: `}}'");
4404 test_buffering (enum sarif_version version
)
4406 test_sarif_diagnostic_context
dc ("test.c", version
);
4408 diagnostic_buffer
buf_a (dc
);
4409 diagnostic_buffer
buf_b (dc
);
4411 rich_location
rich_loc (line_table
, UNKNOWN_LOCATION
);
4413 ASSERT_EQ (dc
.diagnostic_count (DK_ERROR
), 0);
4414 ASSERT_EQ (buf_a
.diagnostic_count (DK_ERROR
), 0);
4415 ASSERT_EQ (buf_b
.diagnostic_count (DK_ERROR
), 0);
4416 ASSERT_EQ (dc
.num_results (), 0);
4417 ASSERT_TRUE (buf_a
.empty_p ());
4418 ASSERT_TRUE (buf_b
.empty_p ());
4420 /* Unbuffered diagnostic. */
4422 dc
.report (DK_ERROR
, rich_loc
, nullptr, 0,
4425 ASSERT_EQ (dc
.diagnostic_count (DK_ERROR
), 1);
4426 ASSERT_EQ (buf_a
.diagnostic_count (DK_ERROR
), 0);
4427 ASSERT_EQ (buf_b
.diagnostic_count (DK_ERROR
), 0);
4428 ASSERT_EQ (dc
.num_results (), 1);
4429 sarif_result
&result_obj
= dc
.get_result (0);
4430 auto message_obj
= get_message_from_result (result_obj
);
4431 ASSERT_JSON_STRING_PROPERTY_EQ (message_obj
, "text",
4433 ASSERT_TRUE (buf_a
.empty_p ());
4434 ASSERT_TRUE (buf_b
.empty_p ());
4437 /* Buffer diagnostic into buffer A. */
4439 dc
.set_diagnostic_buffer (&buf_a
);
4440 dc
.report (DK_ERROR
, rich_loc
, nullptr, 0,
4441 "message in buffer a");
4442 ASSERT_EQ (dc
.diagnostic_count (DK_ERROR
), 1);
4443 ASSERT_EQ (buf_a
.diagnostic_count (DK_ERROR
), 1);
4444 ASSERT_EQ (buf_b
.diagnostic_count (DK_ERROR
), 0);
4445 ASSERT_EQ (dc
.num_results (), 1);
4446 ASSERT_FALSE (buf_a
.empty_p ());
4447 ASSERT_TRUE (buf_b
.empty_p ());
4450 /* Buffer diagnostic into buffer B. */
4452 dc
.set_diagnostic_buffer (&buf_b
);
4453 dc
.report (DK_ERROR
, rich_loc
, nullptr, 0,
4454 "message in buffer b");
4455 ASSERT_EQ (dc
.diagnostic_count (DK_ERROR
), 1);
4456 ASSERT_EQ (buf_a
.diagnostic_count (DK_ERROR
), 1);
4457 ASSERT_EQ (buf_b
.diagnostic_count (DK_ERROR
), 1);
4458 ASSERT_EQ (dc
.num_results (), 1);
4459 ASSERT_FALSE (buf_a
.empty_p ());
4460 ASSERT_FALSE (buf_b
.empty_p ());
4463 /* Flush buffer B to dc. */
4465 dc
.flush_diagnostic_buffer (buf_b
);
4466 ASSERT_EQ (dc
.diagnostic_count (DK_ERROR
), 2);
4467 ASSERT_EQ (buf_a
.diagnostic_count (DK_ERROR
), 1);
4468 ASSERT_EQ (buf_b
.diagnostic_count (DK_ERROR
), 0);
4469 ASSERT_EQ (dc
.num_results (), 2);
4470 sarif_result
&result_1_obj
= dc
.get_result (1);
4471 auto message_1_obj
= get_message_from_result (result_1_obj
);
4472 ASSERT_JSON_STRING_PROPERTY_EQ (message_1_obj
, "text",
4473 "message in buffer b");
4474 ASSERT_FALSE (buf_a
.empty_p ());
4475 ASSERT_TRUE (buf_b
.empty_p ());
4478 /* Clear buffer A. */
4480 dc
.clear_diagnostic_buffer (buf_a
);
4481 ASSERT_EQ (dc
.diagnostic_count (DK_ERROR
), 2);
4482 ASSERT_EQ (buf_a
.diagnostic_count (DK_ERROR
), 0);
4483 ASSERT_EQ (buf_b
.diagnostic_count (DK_ERROR
), 0);
4484 ASSERT_EQ (dc
.num_results (), 2);
4485 ASSERT_TRUE (buf_a
.empty_p ());
4486 ASSERT_TRUE (buf_b
.empty_p ());
4491 run_tests_per_version (const line_table_case
&case_
)
4493 for (int version_idx
= 0;
4494 version_idx
< (int)sarif_version::num_versions
;
4497 enum sarif_version version
4498 = static_cast<enum sarif_version
> (version_idx
);
4500 test_make_location_object (case_
, version
);
4501 test_simple_log_2 (case_
, version
);
4505 /* Run all of the selftests within this file. */
4508 diagnostic_format_sarif_cc_tests ()
4510 for (int version_idx
= 0;
4511 version_idx
< (int)sarif_version::num_versions
;
4514 enum sarif_version version
4515 = static_cast<enum sarif_version
> (version_idx
);
4517 test_simple_log (version
);
4518 test_message_with_embedded_link (version
);
4519 test_message_with_braces (version
);
4520 test_buffering (version
);
4523 /* Run tests per (line-table-case, SARIF version) pair. */
4524 for_each_line_table_case (run_tests_per_version
);
4527 } // namespace selftest
4529 #endif /* CHECKING_P */