Line data Source code
1 : /* SARIF output for diagnostics
2 : Copyright (C) 2018-2026 Free Software Foundation, Inc.
3 : Contributed by David Malcolm <dmalcolm@redhat.com>.
4 :
5 : This file is part of GCC.
6 :
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
10 : version.
11 :
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
15 : for more details.
16 :
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/>. */
20 :
21 : #include "config.h"
22 :
23 : #ifdef __MINGW32__
24 : #include <winsock2.h>
25 : #ifdef HAVE_AFUNIX_H
26 : #include <afunix.h>
27 : #else
28 : struct sockaddr_un {
29 : ADDRESS_FAMILY sun_family;
30 : char sun_path[108];
31 : };
32 : #endif
33 : #else
34 : #include <sys/un.h>
35 : #include <sys/socket.h>
36 : #endif
37 :
38 : #define INCLUDE_LIST
39 : #define INCLUDE_MAP
40 : #define INCLUDE_SET
41 : #define INCLUDE_STRING
42 : #define INCLUDE_VECTOR
43 : #include "system.h"
44 : #include "coretypes.h"
45 : #include "diagnostics/metadata.h"
46 : #include "diagnostics/digraphs.h"
47 : #include "diagnostics/state-graphs.h"
48 : #include "diagnostics/paths.h"
49 : #include "diagnostics/sink.h"
50 : #include "diagnostics/buffering.h"
51 : #include "diagnostics/dumping.h"
52 : #include "diagnostics/logging.h"
53 : #include "json.h"
54 : #include "cpplib.h"
55 : #include "diagnostics/logical-locations.h"
56 : #include "diagnostics/client-data-hooks.h"
57 : #include "diagnostics/diagram.h"
58 : #include "text-art/canvas.h"
59 : #include "diagnostics/sarif-sink.h"
60 : #include "diagnostics/text-sink.h"
61 : #include "ordered-hash-map.h"
62 : #include "sbitmap.h"
63 : #include "selftest.h"
64 : #include "diagnostics/selftest-context.h"
65 : #include "diagnostics/selftest-source-printing.h"
66 : #include "selftest-json.h"
67 : #include "text-range-label.h"
68 : #include "pretty-print-format-impl.h"
69 : #include "pretty-print-urlifier.h"
70 : #include "demangle.h"
71 : #include "backtrace.h"
72 : #include "xml.h"
73 : #include "intl.h"
74 :
75 : namespace diagnostics {
76 :
77 : /* A json::array where the values are "unique" as per
78 : SARIF v2.1.0 section 3.7.3 ("Array properties with unique values"). */
79 :
80 : template <typename JsonElementType>
81 : class sarif_array_of_unique : public json::array
82 : {
83 : public:
84 586 : size_t append_uniquely (std::unique_ptr<JsonElementType> val)
85 : {
86 : /* This should be O(log(n)) due to the std::map. */
87 586 : auto search = m_index_by_value.find (val.get ());
88 586 : if (search != m_index_by_value.end())
89 273 : return (*search).second;
90 :
91 313 : const size_t insertion_idx = size ();
92 313 : m_index_by_value.insert ({val.get (), insertion_idx});
93 313 : append (std::move (val));
94 313 : return insertion_idx;
95 : }
96 :
97 : /* For ease of reading output, add "index": idx to all
98 : objects in the array.
99 : We don't do this until we've added everything, since
100 : the "index" property would otherwise confuse the
101 : comparison against new elements. */
102 67 : void add_explicit_index_values ()
103 : {
104 372 : for (size_t idx = 0; idx < length (); ++idx)
105 305 : if (json::object *obj = get (idx)->dyn_cast_object ())
106 305 : obj->set_integer ("index", idx);
107 67 : }
108 :
109 : JsonElementType *
110 : get_element (size_t i) const
111 : {
112 : return static_cast<JsonElementType *> ((*this)[i]);
113 : }
114 :
115 : private:
116 : struct comparator_t {
117 5370 : bool operator () (const json::value *a, const json::value *b) const
118 : {
119 5370 : gcc_assert (a);
120 5370 : gcc_assert (b);
121 5370 : return json::value::compare (*a, *b) < 0;
122 : }
123 : };
124 :
125 : // json::value * here is borrowed from m_elements
126 : std::map<json::value *, int, comparator_t> m_index_by_value;
127 : };
128 :
129 : /* Forward decls. */
130 : class sarif_builder;
131 : class content_renderer;
132 : class escape_nonascii_renderer;
133 :
134 : /* Subclasses of sarif_object.
135 : Keep these in order of their descriptions in the specification. */
136 : class sarif_artifact_content; // 3.3
137 : class sarif_artifact_location; // 3.4
138 : class sarif_message; // 3.11
139 : class sarif_multiformat_message_string; // 3.12
140 : class sarif_log; // 3.13
141 : class sarif_run; // 3.14
142 : class sarif_tool; // 3.18
143 : class sarif_tool_component; // 3.19
144 : class sarif_invocation; // 3.20
145 : class sarif_artifact; // 3.24
146 : class sarif_location_manager; // not in the spec
147 : class sarif_result; // 3.27
148 : class sarif_location; // 3.28
149 : class sarif_physical_location; // 3.29
150 : class sarif_region; // 3.30
151 : class sarif_logical_location; // 3.33
152 : class sarif_location_relationship; // 3.34
153 : class sarif_code_flow; // 3.36
154 : class sarif_thread_flow; // 3.37
155 : class sarif_thread_flow_location; // 3.38
156 : class sarif_reporting_descriptor; // 3.49
157 : class sarif_reporting_descriptor_reference; // 3.53
158 : class sarif_tool_component_reference; // 3.54
159 : class sarif_fix; // 3.55
160 : class sarif_artifact_change; // 3.56
161 : class sarif_replacement; // 3.57
162 : class sarif_ice_notification; // 3.58
163 :
164 : // Valid values for locationRelationship's "kinds" property (3.34.3)
165 :
166 : enum class location_relationship_kind
167 : {
168 : includes,
169 : is_included_by,
170 : relevant,
171 :
172 : NUM_KINDS
173 : };
174 :
175 : /* Declarations of subclasses of sarif_object.
176 : Keep these in order of their descriptions in the specification. */
177 :
178 : /* Subclass of sarif_object for SARIF "artifactContent" objects
179 : (SARIF v2.1.0 section 3.3). */
180 :
181 909 : class sarif_artifact_content : public sarif_object {};
182 :
183 : /* Subclass of sarif_object for SARIF "artifactLocation" objects
184 : (SARIF v2.1.0 section 3.4). */
185 :
186 1758 : class sarif_artifact_location : public sarif_object {};
187 :
188 : /* Subclass of sarif_object for SARIF "message" objects
189 : (SARIF v2.1.0 section 3.11). */
190 :
191 2031 : class sarif_message : public sarif_object {};
192 :
193 : /* Subclass of sarif_object for SARIF "multiformatMessageString" objects
194 : (SARIF v2.1.0 section 3.12). */
195 :
196 158 : class sarif_multiformat_message_string : public sarif_object {};
197 :
198 : /* Subclass of sarif_object for SARIF "log" objects
199 : (SARIF v2.1.0 section 3.13). */
200 :
201 282 : class sarif_log : public sarif_object {};
202 :
203 : /* Subclass of sarif_object for SARIF "run" objects
204 : (SARIF v2.1.0 section 3.14). */
205 :
206 282 : class sarif_run : public sarif_object {};
207 :
208 : /* Subclass of sarif_object for SARIF "tool" objects
209 : (SARIF v2.1.0 section 3.18). */
210 :
211 282 : class sarif_tool : public sarif_object {};
212 :
213 : /* Subclass of sarif_object for SARIF "toolComponent" objects
214 : (SARIF v2.1.0 section 3.19). */
215 :
216 315 : class sarif_tool_component : public sarif_object {};
217 :
218 : /* Make a JSON string for the current date and time.
219 : See SARIF v2.1.0 section 3.9 "Date/time properties".
220 : Given that we don't run at the very beginning/end of the
221 : process, it doesn't make sense to be more accurate than
222 : the current second. */
223 :
224 : static std::unique_ptr<json::string>
225 700 : make_date_time_string_for_current_time ()
226 : {
227 700 : time_t t = time (nullptr);
228 700 : struct tm *tm = gmtime (&t);
229 700 : char buf[256];
230 700 : snprintf (buf, sizeof (buf) - 1,
231 : ("%04i-%02i-%02iT"
232 : "%02i:%02i:%02iZ"),
233 700 : tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
234 : tm->tm_hour, tm->tm_min, tm->tm_sec);
235 700 : return std::make_unique<json::string> (buf);
236 : }
237 :
238 : /* Subclass of sarif_object for SARIF "invocation" objects
239 : (SARIF v2.1.0 section 3.20). */
240 :
241 : class sarif_invocation : public sarif_object
242 : {
243 : public:
244 : sarif_invocation (sarif_builder &builder,
245 : const char * const *original_argv);
246 :
247 : void add_notification_for_ice (const diagnostic_info &diagnostic,
248 : sarif_builder &builder,
249 : std::unique_ptr<json::object> backtrace);
250 : void prepare_to_flush (sarif_builder &builder);
251 :
252 : private:
253 : std::unique_ptr<json::array> m_notifications_arr;
254 : bool m_success;
255 : };
256 :
257 : /* Corresponds to values for the SARIF artifact objects "roles" property.
258 : (SARIF v2.1.0 section 3.24.6). */
259 :
260 : enum class diagnostic_artifact_role
261 : {
262 : analysis_target, /* "analysisTarget". */
263 : debug_output_file, /* "debugOutputFile". */
264 : result_file, /* "resultFile". */
265 :
266 : /* "scannedFile" added in 2.2;
267 : see https://github.com/oasis-tcs/sarif-spec/issues/459 */
268 : scanned_file,
269 :
270 : traced_file, /* "tracedFile". */
271 :
272 : NUM_ROLES
273 : };
274 :
275 : /* Subclass of sarif_object for SARIF artifact objects
276 : (SARIF v2.1.0 section 3.24). */
277 :
278 : class sarif_artifact : public sarif_object
279 : {
280 : public:
281 436 : sarif_artifact (const char *filename)
282 872 : : m_filename (filename),
283 872 : m_roles ((unsigned)diagnostic_artifact_role::NUM_ROLES),
284 436 : m_embed_contents (false)
285 : {
286 436 : bitmap_clear (m_roles);
287 436 : }
288 :
289 : void add_role (enum diagnostic_artifact_role role,
290 : bool embed_contents);
291 :
292 300 : bool embed_contents_p () const { return m_embed_contents; }
293 : void populate_contents (sarif_builder &builder);
294 : void populate_roles ();
295 :
296 : private:
297 : const char *m_filename;
298 : auto_sbitmap m_roles;
299 :
300 : /* Flag to track whether this artifact should have a "contents" property
301 : (SARIF v2.1.0 section 3.24.8).
302 : We only add the contents for those artifacts that have a location
303 : referencing them (so that a consumer might want to quote the source). */
304 : bool m_embed_contents;
305 : };
306 :
307 : /* A class for sarif_objects that own a "namespace" of numeric IDs for
308 : managing location objects within them. Currently (SARIF v2.1.0)
309 : this is just for sarif_result (section 3.28.2), but it will likely
310 : eventually also be for notification objects; see
311 : https://github.com/oasis-tcs/sarif-spec/issues/540
312 :
313 : Consider locations with chains of include information e.g.
314 :
315 : > include-chain-1.c:
316 : > #include "include-chain-1.h"
317 :
318 : include-chain-1.h:
319 : | // First set of decls, which will be referenced in notes
320 : | #include "include-chain-1-1.h"
321 : |
322 : | // Second set of decls, which will trigger the errors
323 : | #include "include-chain-1-2.h"
324 :
325 : include-chain-1-1.h:
326 : | int p;
327 : | int q;
328 :
329 : include-chain-1-1.h:
330 : | char p;
331 : | char q;
332 :
333 : GCC's textual output emits:
334 : | In file included from PATH/include-chain-1.h:5,
335 : | from PATH/include-chain-1.c:30:
336 : | PATH/include-chain-1-2.h:1:6: error: conflicting types for 'p'; have 'char'
337 : | 1 | char p;
338 : | | ^
339 : | In file included from PATH/include-chain-1.h:2:
340 : | PATH/include-chain-1-1.h:1:5: note: previous declaration of 'p' with type 'int'
341 : | 1 | int p;
342 : | | ^
343 : | PATH/include-chain-1-2.h:2:6: error: conflicting types for 'q'; have 'char'
344 : | 2 | char q;
345 : | | ^
346 : | PATH/include-chain-1-1.h:2:5: note: previous declaration of 'q' with type 'int'
347 : | 2 | int q;
348 : | | ^
349 :
350 : Whenever a SARIF location is added for a location_t that
351 : was #included from somewhere, we queue up the creation of a SARIF
352 : location for the location of the #include. The worklist of queued
353 : locations is flushed when the result is finished, which lazily creates
354 : any additional related locations for the include chain, and the
355 : relationships between the locations. Doing so can lead to further
356 : include locations being processed. The worklist approach allows us
357 : to lazily explore the relevant part of the directed graph of location_t
358 : values implicit in our line_maps structure, replicating it as a directed
359 : graph of SARIF locations within the SARIF result object, like this:
360 :
361 : [0]: error in include-chain-1-2.h ("conflicting types for 'p'; have 'char'")
362 : [1]: #include "include-chain-1-2.h" in include-chain-1.h
363 : [2]: note in include-chain-1-2.h ("previous declaration of 'p' with type 'int'")
364 : [3]: #include "include-chain-1-1.h" in include-chain-1.h
365 : [4]: #include "include-chain-1.h" in include-chain-1.c
366 :
367 : where we want to capture this "includes" graph in SARIF form:
368 : . +-----------------------------------+ +----------------------------------+
369 : . |"id": 0 | |"id": 2 |
370 : . | error: "conflicting types for 'p';| | note: previous declaration of 'p'|
371 : . | have 'char'"| | | with type 'int'") |
372 : . | in include-chain-1-2.h | | in include-chain-1-1.h |
373 : . +-----------------------------------+ +----------------------------------+
374 : . ^ | ^ |
375 : . includes | | included-by includes | | included-by
376 : . | V | V
377 : . +--------------------------------+ +--------------------------------+
378 : . |"id": 1 | |"id": 3 |
379 : . | #include "include-chain-1-2.h" | | #include "include-chain-1-1.h" |
380 : . | in include-chain-1.h | | in include-chain-1.h |
381 : . +--------------------------------+ +--------------------------------+
382 : . ^ | ^ |
383 : . includes | | included-by includes | | included-by
384 : . | V | V
385 : . +------------------------------------+
386 : . |"id": 4 |
387 : . | The #include "include-chain-1.h" |
388 : . | in include-chain-1.c |
389 : . +------------------------------------+
390 : */
391 :
392 : class sarif_location_manager : public sarif_object
393 : {
394 : public:
395 : /* A worklist of pending actions needed to fully process this object.
396 :
397 : This lets us lazily walk our data structures to build the
398 : directed graph of locations, whilst keeping "notes" at the top
399 : of the "relatedLocations" array, and avoiding the need for
400 : recursion. */
401 : struct worklist_item
402 : {
403 : enum class kind
404 : {
405 : /* Process a #include relationship where m_location_obj
406 : was #included-d at m_where. */
407 : included_from,
408 :
409 : /* Process a location_t that was added as a secondary location
410 : to a rich_location without a label. */
411 : unlabelled_secondary_location
412 : };
413 :
414 27 : worklist_item (sarif_location &location_obj,
415 : enum kind kind,
416 : location_t where)
417 27 : : m_location_obj (location_obj),
418 27 : m_kind (kind),
419 27 : m_where (where)
420 : {
421 : }
422 :
423 : sarif_location &m_location_obj;
424 : enum kind m_kind;
425 : location_t m_where;
426 : };
427 :
428 613 : sarif_location_manager ()
429 1226 : : m_related_locations_arr (nullptr),
430 613 : m_next_location_id (0)
431 : {
432 613 : }
433 :
434 39 : unsigned allocate_location_id ()
435 : {
436 39 : return m_next_location_id++;
437 : }
438 :
439 : virtual void
440 : add_related_location (std::unique_ptr<sarif_location> location_obj,
441 : sarif_builder &builder);
442 :
443 : void
444 : add_relationship_to_worklist (sarif_location &location_obj,
445 : enum worklist_item::kind kind,
446 : location_t where);
447 :
448 : void
449 : process_worklist (sarif_builder &builder);
450 :
451 : void
452 : process_worklist_item (sarif_builder &builder,
453 : const worklist_item &item);
454 : private:
455 : json::array *m_related_locations_arr; // borrowed
456 : unsigned m_next_location_id;
457 :
458 : std::list<worklist_item> m_worklist;
459 : std::map<location_t, sarif_location *> m_included_from_locations;
460 : std::map<location_t, sarif_location *> m_unlabelled_secondary_locations;
461 : };
462 :
463 : /* Subclass of sarif_object for SARIF "result" objects
464 : (SARIF v2.1.0 section 3.27).
465 : Each SARIF result object has its own "namespace" of numeric IDs for
466 : managing location objects (SARIF v2.1.0 section 3.28.2). */
467 :
468 256 : class sarif_result : public sarif_location_manager
469 : {
470 : public:
471 607 : sarif_result (unsigned idx_within_parent)
472 479 : : m_idx_within_parent (idx_within_parent)
473 : {}
474 :
475 17 : unsigned get_index_within_parent () const { return m_idx_within_parent; }
476 :
477 : void
478 : on_nested_diagnostic (const diagnostic_info &diagnostic,
479 : enum kind orig_diag_kind,
480 : sarif_builder &builder);
481 : void on_diagram (const diagram &d,
482 : sarif_builder &builder);
483 :
484 : private:
485 : const unsigned m_idx_within_parent;
486 : };
487 :
488 : /* Subclass of sarif_object for SARIF "location" objects
489 : (SARIF v2.1.0 section 3.28).
490 : A location object can have an "id" which must be unique within
491 : the enclosing result, if any (see SARIF v2.1.0 section 3.28.2). */
492 :
493 957 : class sarif_location : public sarif_object
494 : {
495 : public:
496 : long lazily_add_id (sarif_location_manager &loc_mgr);
497 : long get_id () const;
498 :
499 : void lazily_add_relationship (sarif_location &target,
500 : enum location_relationship_kind kind,
501 : sarif_location_manager &loc_mgr);
502 :
503 : private:
504 : sarif_location_relationship &
505 : lazily_add_relationship_object (sarif_location &target,
506 : sarif_location_manager &loc_mgr);
507 :
508 : json::array &lazily_add_relationships_array ();
509 :
510 : std::map<sarif_location *,
511 : sarif_location_relationship *> m_relationships_map;
512 : };
513 :
514 : /* Subclass of sarif_object for SARIF "physicalLocation" objects
515 : (SARIF v2.1.0 section 3.29). */
516 :
517 851 : class sarif_physical_location : public sarif_object {};
518 :
519 : /* Subclass of sarif_object for SARIF "region" objects
520 : (SARIF v2.1.0 section 3.30). */
521 :
522 2113 : class sarif_region : public sarif_object {};
523 :
524 : /* Subclass of sarif_object for SARIF "logicalLocation" objects
525 : (SARIF v2.1.0 section 3.33). */
526 :
527 873 : class sarif_logical_location : public sarif_object
528 : {
529 : };
530 :
531 : /* Subclass of sarif_object for SARIF "locationRelationship" objects
532 : (SARIF v2.1.0 section 3.34). */
533 :
534 : class sarif_location_relationship : public sarif_object
535 : {
536 : public:
537 : sarif_location_relationship (sarif_location &target,
538 : sarif_location_manager &loc_mgr);
539 :
540 : long get_target_id () const;
541 :
542 : void lazily_add_kind (enum location_relationship_kind kind);
543 :
544 : private:
545 : auto_sbitmap m_kinds;
546 : };
547 :
548 : /* Subclass of sarif_object for SARIF "codeFlow" objects
549 : (SARIF v2.1.0 section 3.36). */
550 :
551 : class sarif_code_flow : public sarif_object
552 : {
553 : public:
554 : sarif_code_flow (sarif_result &parent,
555 : unsigned idx_within_parent);
556 :
557 17 : sarif_result &get_parent () const { return m_parent; }
558 17 : unsigned get_index_within_parent () const { return m_idx_within_parent; }
559 :
560 : sarif_thread_flow &
561 : get_or_append_thread_flow (const paths::thread &thread,
562 : paths::thread_id_t thread_id);
563 :
564 : sarif_thread_flow &
565 : get_thread_flow (paths::thread_id_t thread_id);
566 :
567 : void add_location (sarif_thread_flow_location &);
568 :
569 : sarif_thread_flow_location &
570 : get_thread_flow_loc_obj (paths::event_id_t event_id) const;
571 :
572 : private:
573 : sarif_result &m_parent;
574 : const unsigned m_idx_within_parent;
575 :
576 : hash_map<int_hash<paths::thread_id_t, -1, -2>,
577 : sarif_thread_flow *> m_thread_id_map; // borrowed ptr
578 : json::array *m_thread_flows_arr; // borrowed
579 :
580 : /* Vec of borrowed ptr, allowing for going easily from
581 : an event_id to the corresponding threadFlowLocation object. */
582 : std::vector<sarif_thread_flow_location *> m_all_tfl_objs;
583 : };
584 :
585 : /* Subclass of sarif_object for SARIF "threadFlow" objects
586 : (SARIF v2.1.0 section 3.37). */
587 :
588 : class sarif_thread_flow : public sarif_object
589 : {
590 : public:
591 : sarif_thread_flow (sarif_code_flow &parent,
592 : const paths::thread &thread,
593 : unsigned idx_within_parent);
594 :
595 17 : sarif_code_flow &get_parent () const { return m_parent; }
596 17 : unsigned get_index_within_parent () const { return m_idx_within_parent; }
597 :
598 : sarif_thread_flow_location &add_location ();
599 :
600 : private:
601 : sarif_code_flow &m_parent;
602 : json::array *m_locations_arr; // borrowed
603 : const unsigned m_idx_within_parent;
604 : };
605 :
606 : /* Subclass of sarif_object for SARIF "threadFlowLocation" objects
607 : (SARIF v2.1.0 section 3.38). */
608 :
609 : class sarif_thread_flow_location : public sarif_object
610 : {
611 : public:
612 164 : sarif_thread_flow_location (sarif_thread_flow &parent,
613 : unsigned idx_within_parent)
614 328 : : m_parent (parent),
615 328 : m_idx_within_parent (idx_within_parent)
616 : {
617 : }
618 :
619 17 : sarif_thread_flow &get_parent () const { return m_parent; }
620 17 : unsigned get_index_within_parent () const { return m_idx_within_parent; }
621 :
622 : private:
623 : sarif_thread_flow &m_parent;
624 : const unsigned m_idx_within_parent;
625 : };
626 :
627 : /* Subclass of sarif_object for SARIF "reportingDescriptor" objects
628 : (SARIF v2.1.0 section 3.49). */
629 :
630 89 : class sarif_reporting_descriptor : public sarif_object {};
631 :
632 : /* Subclass of sarif_object for SARIF "reportingDescriptorReference" objects
633 : (SARIF v2.1.0 section 3.53). */
634 :
635 25 : class sarif_reporting_descriptor_reference : public sarif_object {};
636 :
637 : /* Subclass of sarif_object for SARIF "toolComponentReference" objects
638 : (SARIF v2.1.0 section 3.54). */
639 :
640 25 : class sarif_tool_component_reference : public sarif_object {};
641 :
642 : /* Subclass of sarif_object for SARIF "fix" objects
643 : (SARIF v2.1.0 section 3.55). */
644 :
645 9 : class sarif_fix : public sarif_object {};
646 :
647 : /* Subclass of sarif_object for SARIF "artifactChange" objects
648 : (SARIF v2.1.0 section 3.56). */
649 :
650 9 : class sarif_artifact_change : public sarif_object {};
651 :
652 : /* Subclass of sarif_object for SARIF "replacement" objects
653 : (SARIF v2.1.0 section 3.57). */
654 :
655 9 : class sarif_replacement : public sarif_object {};
656 :
657 : /* Subclass of sarif_object for SARIF "notification" objects
658 : (SARIF v2.1.0 section 3.58).
659 :
660 : This subclass is specifically for notifying when an
661 : internal compiler error occurs. */
662 :
663 : class sarif_ice_notification : public sarif_location_manager
664 : {
665 : public:
666 : sarif_ice_notification (const diagnostic_info &diagnostic,
667 : sarif_builder &builder,
668 : std::unique_ptr<json::object> backtrace);
669 :
670 : void
671 : add_related_location (std::unique_ptr<sarif_location> location_obj,
672 : sarif_builder &builder) final override;
673 : };
674 :
675 : /* Abstract base class for use when making an "artifactContent"
676 : object (SARIF v2.1.0 section 3.3): generate a value for the
677 : 3.3.4 "rendered" property.
678 : Can return nullptr, for "no property". */
679 :
680 768 : class content_renderer
681 : {
682 : public:
683 768 : virtual ~content_renderer () {}
684 :
685 : virtual std::unique_ptr<sarif_multiformat_message_string>
686 : render (const sarif_builder &builder) const = 0;
687 : };
688 :
689 : /* Concrete buffering implementation subclass for SARIF output. */
690 :
691 : class sarif_sink_buffer : public per_sink_buffer
692 : {
693 : public:
694 : friend class sarif_sink;
695 :
696 17 : sarif_sink_buffer (sarif_builder &builder)
697 17 : : m_builder (builder)
698 : {}
699 :
700 : void dump (FILE *out, int indent) const final override;
701 : bool empty_p () const final override;
702 : void move_to (per_sink_buffer &dest) final override;
703 : void clear () final override;
704 : void flush () final override;
705 :
706 21 : void add_result (std::unique_ptr<sarif_result> result)
707 : {
708 42 : m_results.push_back (std::move (result));
709 : }
710 :
711 : size_t num_results () const { return m_results.size (); }
712 : sarif_result &get_result (size_t idx) { return *m_results[idx]; }
713 :
714 : private:
715 : sarif_builder &m_builder;
716 : std::vector<std::unique_ptr<sarif_result>> m_results;
717 : };
718 :
719 : /* Classes for abstracting away JSON vs other serialization formats. */
720 :
721 : // class sarif_serialization_format_json : public sarif_serialization_format
722 :
723 : void
724 114 : sarif_serialization_format_json::write_to_file (FILE *outf,
725 : const json::value &top)
726 : {
727 114 : top.dump (outf, m_formatted);
728 114 : fprintf (outf, "\n");
729 114 : }
730 :
731 : void
732 0 : sarif_serialization_format_json::dump (FILE *outfile, int indent) const
733 : {
734 0 : dumping::emit_indent (outfile, indent);
735 0 : fprintf (outfile, "json\n");
736 0 : DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_formatted);
737 0 : }
738 :
739 : /* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr
740 : and -fdiagnostics-format=sarif-file).
741 :
742 : As diagnostics occur, we build "result" JSON objects, and
743 : accumulate state:
744 : - which source files are referenced
745 : - which warnings are emitted
746 : - which CWEs are used
747 :
748 : At the end of the compile, we use the above to build the full SARIF
749 : object tree, adding the result objects to the correct place, and
750 : creating objects for the various source files, warnings and CWEs
751 : referenced.
752 :
753 : Implemented:
754 : - fix-it hints
755 : - CWE metadata
756 : - diagnostic groups (see limitations below)
757 : - logical locations (e.g. cfun)
758 : - labelled ranges (as annotations)
759 : - secondary ranges without labels (as related locations)
760 :
761 : Known limitations:
762 : - GCC supports nesting of diagnostics (one-deep nesting via
763 : auto_diagnostic_group, and arbitrary nesting via
764 : auto_diagnostic_nesting_level). These are captured in the SARIF
765 : as related locations, and so we only capture location and message
766 : information from such nested diagnostics (e.g. we ignore fix-it
767 : hints on them). Diagnostics within an auto_diagnostic_nesting_level
768 : have their nesting level captured as a property.
769 : - although we capture command-line arguments (section 3.20.2), we don't
770 : yet capture response files.
771 : - doesn't capture "artifact.encoding" property
772 : (SARIF v2.1.0 section 3.24.9).
773 : - doesn't capture hashes of the source files
774 : ("artifact.hashes" property (SARIF v2.1.0 section 3.24.11).
775 : - doesn't capture the "analysisTarget" property
776 : (SARIF v2.1.0 section 3.27.13).
777 : - doesn't capture -Werror cleanly
778 : - doesn't capture inlining information (can SARIF handle this?)
779 : - doesn't capture macro expansion information (can SARIF handle this?).
780 : - doesn't capture any diagnostics::metadata::rules associated with
781 : a diagnostic. */
782 :
783 : class sarif_builder
784 : {
785 : public:
786 : friend class sarif_sink_buffer;
787 :
788 : sarif_builder (diagnostics::context &dc,
789 : pretty_printer &printer,
790 : const line_maps *line_maps,
791 : std::unique_ptr<sarif_serialization_format> serialization_format,
792 : const sarif_generation_options &sarif_gen_opts);
793 : ~sarif_builder ();
794 :
795 : void dump (FILE *out, int indent) const;
796 :
797 290 : void set_printer (pretty_printer &printer)
798 : {
799 290 : m_printer = &printer;
800 : }
801 :
802 : const logical_locations::manager *
803 1147 : get_logical_location_manager () const
804 : {
805 1147 : if (auto client_data_hooks = m_context.get_client_data_hooks ())
806 1147 : return client_data_hooks->get_logical_location_manager ();
807 : return nullptr;
808 : }
809 :
810 : void
811 : set_main_input_filename (const char *name);
812 :
813 : void on_report_diagnostic (const diagnostic_info &diagnostic,
814 : enum kind orig_diag_kind,
815 : sarif_sink_buffer *buffer);
816 : void emit_diagram (const diagram &d);
817 : void end_group ();
818 :
819 : void
820 : report_global_digraph (const lazily_created<digraphs::digraph> &);
821 :
822 : void
823 : report_digraph_for_logical_location (const lazily_created<digraphs::digraph> &ldg,
824 : logical_locations::key);
825 :
826 290 : std::unique_ptr<sarif_result> take_current_result ()
827 : {
828 290 : return std::move (m_cur_group_result);
829 : }
830 :
831 : std::unique_ptr<sarif_log> flush_to_object ();
832 : void flush_to_file (FILE *outf);
833 :
834 : std::unique_ptr<json::array>
835 : make_locations_arr (sarif_location_manager &loc_mgr,
836 : const diagnostic_info &diagnostic,
837 : enum diagnostic_artifact_role role);
838 : std::unique_ptr<sarif_location>
839 : make_location_object (sarif_location_manager *loc_mgr,
840 : const rich_location &rich_loc,
841 : logical_locations::key logical_loc,
842 : enum diagnostic_artifact_role role);
843 : std::unique_ptr<sarif_location>
844 : make_location_object (sarif_location_manager &loc_mgr,
845 : location_t where,
846 : enum diagnostic_artifact_role role);
847 : std::unique_ptr<sarif_message>
848 : make_message_object (const char *msg) const;
849 : std::unique_ptr<sarif_message>
850 : make_message_object_for_diagram (const diagram &d);
851 : std::unique_ptr<sarif_artifact_content>
852 : maybe_make_artifact_content_object (const char *filename) const;
853 :
854 : std::unique_ptr<sarif_artifact_location>
855 : make_artifact_location_object (const char *filename);
856 :
857 : const sarif_code_flow *
858 114 : get_code_flow_for_event_ids () const
859 : {
860 114 : return m_current_code_flow;
861 : }
862 :
863 404 : diagnostics::context &get_context () const { return m_context; }
864 410 : pretty_printer *get_printer () const { return m_printer; }
865 290 : token_printer &get_token_printer () { return m_token_printer; }
866 564 : enum sarif_version get_version () const { return m_sarif_gen_opts.m_version; }
867 :
868 96 : size_t num_results () const { return m_results_array->size (); }
869 16 : sarif_result &get_result (size_t idx)
870 : {
871 16 : auto element = (*m_results_array)[idx];
872 16 : gcc_assert (element);
873 16 : return *static_cast<sarif_result *> (element);
874 : }
875 :
876 : const sarif_generation_options &get_opts () const { return m_sarif_gen_opts; }
877 :
878 : std::unique_ptr<sarif_logical_location>
879 : make_minimal_sarif_logical_location (logical_locations::key);
880 :
881 : private:
882 418 : class sarif_token_printer : public token_printer
883 : {
884 : public:
885 418 : sarif_token_printer (sarif_builder &builder)
886 418 : : m_builder (builder)
887 : {
888 : }
889 : void print_tokens (pretty_printer *pp,
890 : const pp_token_list &tokens) final override;
891 : private:
892 : sarif_builder &m_builder;
893 : };
894 :
895 : std::unique_ptr<sarif_result>
896 : make_result_object (const diagnostic_info &diagnostic,
897 : enum kind orig_diag_kind,
898 : unsigned idx_within_parent);
899 : void
900 : add_any_include_chain (sarif_location_manager &loc_mgr,
901 : sarif_location &location_obj,
902 : location_t where);
903 : void
904 : set_any_logical_locs_arr (sarif_location &location_obj,
905 : logical_locations::key logical_loc);
906 : std::unique_ptr<sarif_location>
907 : make_location_object (sarif_location_manager &loc_mgr,
908 : const paths::event &event,
909 : enum diagnostic_artifact_role role);
910 : std::unique_ptr<sarif_code_flow>
911 : make_code_flow_object (sarif_result &result,
912 : unsigned idx_within_parent,
913 : const paths::path &path);
914 : void
915 : populate_thread_flow_location_object (sarif_result &result,
916 : sarif_thread_flow_location &thread_flow_loc_obj,
917 : const paths::event &event,
918 : int event_execution_idx);
919 : std::unique_ptr<json::array>
920 : maybe_make_kinds_array (paths::event::meaning m) const;
921 : std::unique_ptr<sarif_physical_location>
922 : maybe_make_physical_location_object (location_t loc,
923 : enum diagnostic_artifact_role role,
924 : int column_override,
925 : const content_renderer *snippet_renderer);
926 : std::unique_ptr<sarif_artifact_location>
927 : make_artifact_location_object (location_t loc);
928 : std::unique_ptr<sarif_artifact_location>
929 : make_artifact_location_object_for_pwd () const;
930 : std::unique_ptr<sarif_region>
931 : maybe_make_region_object (location_t loc,
932 : int column_override) const;
933 : std::unique_ptr<sarif_region>
934 : maybe_make_region_object_for_context (location_t loc,
935 : const content_renderer *snippet_renderer) const;
936 : std::unique_ptr<sarif_region>
937 : make_region_object_for_hint (const fixit_hint &hint) const;
938 :
939 : int
940 : ensure_sarif_logical_location_for (logical_locations::key k);
941 :
942 : std::unique_ptr<sarif_multiformat_message_string>
943 : make_multiformat_message_string (const char *msg) const;
944 : std::unique_ptr<sarif_log>
945 : make_top_level_object (std::unique_ptr<sarif_invocation> invocation_obj,
946 : std::unique_ptr<json::array> results);
947 : std::unique_ptr<sarif_run>
948 : make_run_object (std::unique_ptr<sarif_invocation> invocation_obj,
949 : std::unique_ptr<json::array> results);
950 : std::unique_ptr<sarif_tool>
951 : make_tool_object ();
952 : std::unique_ptr<sarif_tool_component>
953 : make_driver_tool_component_object ();
954 : std::unique_ptr<json::array> maybe_make_taxonomies_array () const;
955 : std::unique_ptr<sarif_tool_component>
956 : maybe_make_cwe_taxonomy_object () const;
957 : std::unique_ptr<sarif_tool_component_reference>
958 : make_tool_component_reference_object_for_cwe () const;
959 : std::unique_ptr<sarif_reporting_descriptor>
960 : make_reporting_descriptor_object_for_warning (const diagnostic_info &diagnostic,
961 : enum kind orig_diag_kind,
962 : const char *option_text);
963 : std::unique_ptr<sarif_reporting_descriptor>
964 : make_reporting_descriptor_object_for_cwe_id (int cwe_id) const;
965 : std::unique_ptr<sarif_reporting_descriptor_reference>
966 : make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id);
967 : sarif_artifact &
968 : get_or_create_artifact (const char *filename,
969 : enum diagnostic_artifact_role role,
970 : bool embed_contents);
971 : char *
972 : get_source_lines (const char *filename,
973 : int start_line,
974 : int end_line) const;
975 : std::unique_ptr<sarif_artifact_content>
976 : maybe_make_artifact_content_object (const char *filename,
977 : int start_line,
978 : int end_line,
979 : const content_renderer *r) const;
980 : std::unique_ptr<sarif_message>
981 : make_message_describing_fix_it_hint (const fixit_hint &hint) const;
982 : std::unique_ptr<sarif_fix>
983 : make_fix_object (const rich_location &rich_loc);
984 : std::unique_ptr<sarif_artifact_change>
985 : make_artifact_change_object (const rich_location &richloc);
986 : std::unique_ptr<sarif_replacement>
987 : make_replacement_object (const fixit_hint &hint) const;
988 : std::unique_ptr<sarif_artifact_content>
989 : make_artifact_content_object (const char *text) const;
990 : int get_sarif_column (expanded_location exploc) const;
991 :
992 : std::unique_ptr<json::object>
993 : make_stack_from_backtrace ();
994 :
995 : diagnostics::context &m_context;
996 : pretty_printer *m_printer;
997 : const line_maps *m_line_maps;
998 : sarif_token_printer m_token_printer;
999 :
1000 : /* The JSON object for the invocation object. */
1001 : std::unique_ptr<sarif_invocation> m_invocation_obj;
1002 :
1003 : /* The JSON array of pending diagnostics. */
1004 : std::unique_ptr<json::array> m_results_array;
1005 :
1006 : /* The JSON object for the result object (if any) in the current
1007 : diagnostic group. */
1008 : std::unique_ptr<sarif_result> m_cur_group_result;
1009 :
1010 : /* Ideally we'd use std::unique_ptr<sarif_artifact> here, but I had
1011 : trouble getting this to work when building with GCC 4.8. */
1012 : ordered_hash_map <nofree_string_hash,
1013 : sarif_artifact *> m_filename_to_artifact_map;
1014 :
1015 : bool m_seen_any_relative_paths;
1016 : std::set<std::string> m_rule_id_set;
1017 : std::unique_ptr<json::array> m_rules_arr;
1018 :
1019 : /* The set of all CWE IDs we've seen, if any. */
1020 : hash_set <int_hash <int, 0, 1> > m_cwe_id_set;
1021 :
1022 : std::unique_ptr<sarif_array_of_unique<sarif_logical_location>> m_cached_logical_locs;
1023 :
1024 : std::unique_ptr<sarif_array_of_unique<sarif_graph>> m_run_graphs;
1025 :
1026 : int m_tabstop;
1027 :
1028 : std::unique_ptr<sarif_serialization_format> m_serialization_format;
1029 : const sarif_generation_options m_sarif_gen_opts;
1030 :
1031 : unsigned m_next_result_idx;
1032 : sarif_code_flow *m_current_code_flow;
1033 : };
1034 :
1035 : /* class sarif_object : public json::object. */
1036 :
1037 : sarif_property_bag &
1038 1761 : sarif_object::get_or_create_properties ()
1039 : {
1040 1761 : json::value *properties_val = get ("properties");
1041 1761 : if (properties_val)
1042 : {
1043 141 : if (properties_val->get_kind () == json::JSON_OBJECT)
1044 : return *static_cast <sarif_property_bag *> (properties_val);
1045 : }
1046 :
1047 1620 : sarif_property_bag *bag = new sarif_property_bag ();
1048 1620 : set ("properties", bag);
1049 1620 : return *bag;
1050 : }
1051 :
1052 : /* class sarif_invocation : public sarif_object. */
1053 :
1054 418 : sarif_invocation::sarif_invocation (sarif_builder &builder,
1055 418 : const char * const *original_argv)
1056 418 : : m_notifications_arr (std::make_unique<json::array> ()),
1057 418 : m_success (true)
1058 : {
1059 : // "arguments" property (SARIF v2.1.0 section 3.20.2)
1060 418 : if (original_argv)
1061 : {
1062 114 : auto arguments_arr = std::make_unique<json::array> ();
1063 4315 : for (size_t i = 0; original_argv[i]; ++i)
1064 4201 : arguments_arr->append_string (original_argv[i]);
1065 114 : set<json::array> ("arguments", std::move (arguments_arr));
1066 114 : }
1067 :
1068 : // "workingDirectory" property (SARIF v2.1.0 section 3.20.19)
1069 418 : if (const char *pwd = getpwd ())
1070 836 : set<sarif_artifact_location> ("workingDirectory",
1071 418 : builder.make_artifact_location_object (pwd));
1072 :
1073 : // "startTimeUtc" property (SARIF v2.1.0 section 3.20.7)
1074 836 : set<json::string> ("startTimeUtc",
1075 418 : make_date_time_string_for_current_time ());
1076 418 : }
1077 :
1078 : /* Handle an internal compiler error DIAGNOSTIC.
1079 : Add an object representing the ICE to the notifications array. */
1080 :
1081 : void
1082 6 : sarif_invocation::add_notification_for_ice (const diagnostic_info &diagnostic,
1083 : sarif_builder &builder,
1084 : std::unique_ptr<json::object> backtrace)
1085 : {
1086 6 : m_success = false;
1087 :
1088 6 : auto notification
1089 : = std::make_unique<sarif_ice_notification> (diagnostic,
1090 : builder,
1091 6 : std::move (backtrace));
1092 :
1093 : /* Support for related locations within a notification was added
1094 : in SARIF 2.2; see https://github.com/oasis-tcs/sarif-spec/issues/540 */
1095 6 : if (builder.get_version () >= sarif_version::v2_2_prerelease_2024_08_08)
1096 1 : notification->process_worklist (builder);
1097 :
1098 6 : m_notifications_arr->append<sarif_ice_notification>
1099 6 : (std::move (notification));
1100 6 : }
1101 :
1102 : void
1103 282 : sarif_invocation::prepare_to_flush (sarif_builder &builder)
1104 : {
1105 282 : const context &dc = builder.get_context ();
1106 :
1107 : /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */
1108 282 : if (dc.execution_failed_p ())
1109 201 : m_success = false;
1110 282 : set_bool ("executionSuccessful", m_success);
1111 :
1112 : /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */
1113 282 : set ("toolExecutionNotifications", std::move (m_notifications_arr));
1114 :
1115 : /* Call client hook, allowing it to create a custom property bag for
1116 : this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */
1117 282 : if (auto client_data_hooks = dc.get_client_data_hooks ())
1118 114 : client_data_hooks->add_sarif_invocation_properties (*this);
1119 :
1120 : // "endTimeUtc" property (SARIF v2.1.0 section 3.20.8);
1121 564 : set<json::string> ("endTimeUtc",
1122 282 : make_date_time_string_for_current_time ());
1123 282 : }
1124 :
1125 : /* class sarif_artifact : public sarif_object. */
1126 :
1127 : /* Add ROLE to this artifact's roles.
1128 : If EMBED_CONTENTS is true, then flag that we will attempt to embed the
1129 : contents of this artifact when writing it out. */
1130 :
1131 : void
1132 1141 : sarif_artifact::add_role (enum diagnostic_artifact_role role,
1133 : bool embed_contents)
1134 : {
1135 : /* TODO(SARIF 2.2): "scannedFile" is to be added as a role in SARIF 2.2;
1136 : see https://github.com/oasis-tcs/sarif-spec/issues/459
1137 :
1138 : For now, skip them.
1139 : Ultimately, we probably shouldn't bother embedding the contents
1140 : of such artifacts, just the snippets. */
1141 1141 : if (role == diagnostic_artifact_role::scanned_file)
1142 : return;
1143 :
1144 1120 : if (embed_contents)
1145 830 : m_embed_contents = true;
1146 :
1147 : /* In SARIF v2.1.0 section 3.24.6 "roles" property:
1148 : "resultFile" is for an artifact
1149 : "which the analysis tool was not explicitly instructed to scan",
1150 : whereas "analysisTarget" is for one where the
1151 : "analysis tool was instructed to scan this artifact".
1152 : Hence the latter excludes the former. */
1153 1120 : if (role == diagnostic_artifact_role::result_file)
1154 538 : if (bitmap_bit_p (m_roles, (int)diagnostic_artifact_role::analysis_target))
1155 : return;
1156 :
1157 597 : bitmap_set_bit (m_roles, (int)role);
1158 : }
1159 :
1160 : /* Populate the "contents" property (SARIF v2.1.0 section 3.24.8).
1161 : We do this after initialization to
1162 : (a) ensure that any charset options have been set
1163 : (b) only populate it for artifacts that are referenced by a location. */
1164 :
1165 : void
1166 241 : sarif_artifact::populate_contents (sarif_builder &builder)
1167 : {
1168 241 : if (auto artifact_content_obj
1169 241 : = builder.maybe_make_artifact_content_object (m_filename))
1170 241 : set<sarif_artifact_content> ("contents", std::move (artifact_content_obj));
1171 241 : }
1172 :
1173 : /* Get a string for ROLE corresponding to the
1174 : SARIF v2.1.0 section 3.24.6 "roles" property. */
1175 :
1176 : static const char *
1177 330 : get_artifact_role_string (enum diagnostic_artifact_role role)
1178 : {
1179 330 : switch (role)
1180 : {
1181 0 : default:
1182 0 : gcc_unreachable ();
1183 : case diagnostic_artifact_role::analysis_target:
1184 : return "analysisTarget";
1185 0 : case diagnostic_artifact_role::debug_output_file:
1186 0 : return "debugOutputFile";
1187 13 : case diagnostic_artifact_role::result_file:
1188 13 : return "resultFile";
1189 0 : case diagnostic_artifact_role::scanned_file:
1190 0 : return "scannedFile";
1191 35 : case diagnostic_artifact_role::traced_file:
1192 35 : return "tracedFile";
1193 : }
1194 : }
1195 :
1196 : /* Populate the "roles" property of this sarif_artifact with a new
1197 : json::array for the artifact.roles property (SARIF v2.1.0 section 3.24.6)
1198 : containing strings such as "analysisTarget", "resultFile"
1199 : and/or "tracedFile". */
1200 :
1201 : void
1202 300 : sarif_artifact::populate_roles ()
1203 : {
1204 300 : if (bitmap_empty_p (m_roles))
1205 1 : return;
1206 299 : auto roles_arr (std::make_unique<json::array> ());
1207 1794 : for (int i = 0; i < (int)diagnostic_artifact_role::NUM_ROLES; i++)
1208 1495 : if (bitmap_bit_p (m_roles, i))
1209 : {
1210 330 : enum diagnostic_artifact_role role = (enum diagnostic_artifact_role)i;
1211 330 : roles_arr->append_string (get_artifact_role_string (role));
1212 : }
1213 299 : set<json::array> ("roles", std::move (roles_arr));
1214 299 : }
1215 :
1216 : /* class sarif_location_manager : public sarif_object. */
1217 :
1218 : /* Base implementation of sarif_location_manager::add_related_location vfunc.
1219 :
1220 : Add LOCATION_OBJ to this object's "relatedLocations" array,
1221 : creating it if it doesn't yet exist. */
1222 :
1223 : void
1224 142 : sarif_location_manager::
1225 : add_related_location (std::unique_ptr<sarif_location> location_obj,
1226 : sarif_builder &)
1227 : {
1228 142 : if (!m_related_locations_arr)
1229 : {
1230 41 : m_related_locations_arr = new json::array ();
1231 : /* Give ownership of m_related_locations_arr to json::object;
1232 : keep a borrowed ptr. */
1233 41 : set ("relatedLocations", m_related_locations_arr);
1234 : }
1235 142 : m_related_locations_arr->append (std::move (location_obj));
1236 142 : }
1237 :
1238 : void
1239 27 : sarif_location_manager::
1240 : add_relationship_to_worklist (sarif_location &location_obj,
1241 : enum worklist_item::kind kind,
1242 : location_t where)
1243 : {
1244 54 : m_worklist.push_back (worklist_item (location_obj,
1245 : kind,
1246 27 : where));
1247 27 : }
1248 :
1249 : /* Process all items in this result's worklist.
1250 : Doing so may temporarily add new items to the end
1251 : of the worklist.
1252 : Handling any item should be "lazy", and thus we should
1253 : eventually drain the queue and terminate. */
1254 :
1255 : void
1256 467 : sarif_location_manager::process_worklist (sarif_builder &builder)
1257 : {
1258 493 : while (!m_worklist.empty ())
1259 : {
1260 26 : const worklist_item &item = m_worklist.front ();
1261 26 : process_worklist_item (builder, item);
1262 26 : m_worklist.pop_front ();
1263 : }
1264 467 : }
1265 :
1266 : /* Process one item in this result's worklist, potentially
1267 : adding new items to the end of the worklist. */
1268 :
1269 : void
1270 26 : sarif_location_manager::process_worklist_item (sarif_builder &builder,
1271 : const worklist_item &item)
1272 : {
1273 26 : switch (item.m_kind)
1274 : {
1275 0 : default:
1276 0 : gcc_unreachable ();
1277 22 : case worklist_item::kind::included_from:
1278 22 : {
1279 22 : sarif_location &included_loc_obj = item.m_location_obj;
1280 22 : sarif_location *includer_loc_obj = nullptr;
1281 22 : auto iter = m_included_from_locations.find (item.m_where);
1282 22 : if (iter != m_included_from_locations.end ())
1283 5 : includer_loc_obj = iter->second;
1284 : else
1285 : {
1286 17 : std::unique_ptr<sarif_location> new_loc_obj
1287 : = builder.make_location_object
1288 : (*this,
1289 17 : item.m_where,
1290 17 : diagnostic_artifact_role::scanned_file);
1291 17 : includer_loc_obj = new_loc_obj.get ();
1292 17 : add_related_location (std::move (new_loc_obj), builder);
1293 17 : auto kv
1294 17 : = std::pair<location_t, sarif_location *> (item.m_where,
1295 17 : includer_loc_obj);
1296 17 : m_included_from_locations.insert (kv);
1297 17 : }
1298 :
1299 22 : includer_loc_obj->lazily_add_relationship
1300 22 : (included_loc_obj,
1301 : location_relationship_kind::includes,
1302 : *this);
1303 22 : included_loc_obj.lazily_add_relationship
1304 22 : (*includer_loc_obj,
1305 : location_relationship_kind::is_included_by,
1306 : *this);
1307 : }
1308 22 : break;
1309 4 : case worklist_item::kind::unlabelled_secondary_location:
1310 4 : {
1311 4 : sarif_location &primary_loc_obj = item.m_location_obj;
1312 4 : sarif_location *secondary_loc_obj = nullptr;
1313 4 : auto iter = m_unlabelled_secondary_locations.find (item.m_where);
1314 4 : if (iter != m_unlabelled_secondary_locations.end ())
1315 0 : secondary_loc_obj = iter->second;
1316 : else
1317 : {
1318 4 : std::unique_ptr<sarif_location> new_loc_obj
1319 : = builder.make_location_object
1320 : (*this,
1321 4 : item.m_where,
1322 4 : diagnostic_artifact_role::scanned_file);
1323 4 : secondary_loc_obj = new_loc_obj.get ();
1324 4 : add_related_location (std::move (new_loc_obj), builder);
1325 4 : auto kv
1326 4 : = std::pair<location_t, sarif_location *> (item.m_where,
1327 4 : secondary_loc_obj);
1328 4 : m_unlabelled_secondary_locations.insert (kv);
1329 4 : }
1330 4 : gcc_assert (secondary_loc_obj);
1331 4 : primary_loc_obj.lazily_add_relationship
1332 4 : (*secondary_loc_obj,
1333 : location_relationship_kind::relevant,
1334 : *this);
1335 : }
1336 4 : break;
1337 : }
1338 26 : }
1339 :
1340 : /* class sarif_result : public sarif_location_manager. */
1341 :
1342 : /* Handle secondary diagnostics that occur within a diagnostic group.
1343 : The closest SARIF seems to have to nested diagnostics is the
1344 : "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22),
1345 : so we lazily set this property and populate the array if and when
1346 : secondary diagnostics occur (such as notes to a warning). */
1347 :
1348 : void
1349 117 : sarif_result::on_nested_diagnostic (const diagnostic_info &diagnostic,
1350 : enum kind /*orig_diag_kind*/,
1351 : sarif_builder &builder)
1352 : {
1353 : /* We don't yet generate meaningful logical locations for notes;
1354 : sometimes these will related to current_function_decl, but
1355 : often they won't. */
1356 117 : auto location_obj
1357 117 : = builder.make_location_object (this, *diagnostic.m_richloc,
1358 117 : logical_locations::key (),
1359 117 : diagnostic_artifact_role::result_file);
1360 117 : auto message_obj
1361 117 : = builder.make_message_object (pp_formatted_text (builder.get_printer ()));
1362 117 : pp_clear_output_area (builder.get_printer ());
1363 117 : location_obj->set<sarif_message> ("message", std::move (message_obj));
1364 :
1365 : /* Add nesting level, as per "P3358R0 SARIF for Structured Diagnostics"
1366 : https://wg21.link/P3358R0 */
1367 117 : sarif_property_bag &bag = location_obj->get_or_create_properties ();
1368 117 : bag.set_integer ("nestingLevel",
1369 117 : builder.get_context ().get_diagnostic_nesting_level ());
1370 :
1371 117 : add_related_location (std::move (location_obj), builder);
1372 117 : }
1373 :
1374 : /* Handle diagrams that occur within a diagnostic group.
1375 : The closest thing in SARIF seems to be to add a location to the
1376 : "releatedLocations" property (SARIF v2.1.0 section 3.27.22),
1377 : and to put the diagram into the "message" property of that location
1378 : (SARIF v2.1.0 section 3.28.5). */
1379 :
1380 : void
1381 4 : sarif_result::on_diagram (const diagram &d,
1382 : sarif_builder &builder)
1383 : {
1384 4 : auto location_obj = std::make_unique<sarif_location> ();
1385 4 : auto message_obj = builder.make_message_object_for_diagram (d);
1386 4 : location_obj->set<sarif_message> ("message", std::move (message_obj));
1387 :
1388 4 : add_related_location (std::move (location_obj), builder);
1389 4 : }
1390 :
1391 : /* class sarif_location : public sarif_object. */
1392 :
1393 : /* Ensure this location has an "id" and return it.
1394 : Use LOC_MGR if an id needs to be allocated.
1395 :
1396 : See the "id" property (3.28.2).
1397 :
1398 : We use this to only assign ids to locations that are
1399 : referenced by another sarif object; others have no "id". */
1400 :
1401 : long
1402 48 : sarif_location::lazily_add_id (sarif_location_manager &loc_mgr)
1403 : {
1404 48 : long id = get_id ();
1405 48 : if (id != -1)
1406 : return id;
1407 39 : id = loc_mgr.allocate_location_id ();
1408 39 : set_integer ("id", id);
1409 39 : gcc_assert (id != -1);
1410 39 : return id;
1411 : }
1412 :
1413 : /* Get the id of this location, or -1 if it doesn't have one. */
1414 :
1415 : long
1416 48 : sarif_location::get_id () const
1417 : {
1418 48 : json::value *id = get ("id");
1419 48 : if (!id)
1420 : return -1;
1421 9 : gcc_assert (id->get_kind () == json::JSON_INTEGER);
1422 9 : return static_cast <json::integer_number *> (id)->get ();
1423 : }
1424 :
1425 : // 3.34.3 kinds property
1426 : static const char *
1427 48 : get_string_for_location_relationship_kind (enum location_relationship_kind kind)
1428 : {
1429 48 : switch (kind)
1430 : {
1431 0 : default:
1432 0 : gcc_unreachable ();
1433 : case location_relationship_kind::includes:
1434 : return "includes";
1435 22 : case location_relationship_kind::is_included_by:
1436 22 : return "isIncludedBy";
1437 4 : case location_relationship_kind::relevant:
1438 4 : return "relevant";
1439 : }
1440 : }
1441 :
1442 : /* Lazily populate this location's "relationships" property (3.28.7)
1443 : with the relationship of KIND to TARGET, creating objects
1444 : as necessary.
1445 : Use LOC_MGR for any locations that need "id" values. */
1446 :
1447 : void
1448 48 : sarif_location::lazily_add_relationship (sarif_location &target,
1449 : enum location_relationship_kind kind,
1450 : sarif_location_manager &loc_mgr)
1451 : {
1452 48 : sarif_location_relationship &relationship_obj
1453 48 : = lazily_add_relationship_object (target, loc_mgr);
1454 :
1455 48 : relationship_obj.lazily_add_kind (kind);
1456 48 : }
1457 :
1458 : /* Lazily populate this location's "relationships" property (3.28.7)
1459 : with a location_relationship to TARGET, creating objects
1460 : as necessary.
1461 : Use LOC_MGR for any locations that need "id" values. */
1462 :
1463 : sarif_location_relationship &
1464 48 : sarif_location::lazily_add_relationship_object (sarif_location &target,
1465 : sarif_location_manager &loc_mgr)
1466 : {
1467 : /* See if THIS already has a locationRelationship referencing TARGET. */
1468 48 : auto iter = m_relationships_map.find (&target);
1469 48 : if (iter != m_relationships_map.end ())
1470 : {
1471 : /* We already have a locationRelationship from THIS to TARGET. */
1472 0 : sarif_location_relationship *relationship = iter->second;
1473 0 : gcc_assert (relationship->get_target_id() == target.get_id ());
1474 : return *relationship;
1475 : }
1476 :
1477 : // Ensure that THIS has a "relationships" property (3.28.7).
1478 48 : json::array &relationships_arr = lazily_add_relationships_array ();
1479 :
1480 : /* No existing locationRelationship from THIS to TARGET; make one,
1481 : record it, and add it to the "relationships" array. */
1482 48 : auto relationship_obj
1483 48 : = std::make_unique<sarif_location_relationship> (target, loc_mgr);
1484 48 : sarif_location_relationship *relationship = relationship_obj.get ();
1485 48 : auto kv
1486 : = std::pair<sarif_location *,
1487 48 : sarif_location_relationship *> (&target, relationship);
1488 48 : m_relationships_map.insert (kv);
1489 :
1490 48 : relationships_arr.append (std::move (relationship_obj));
1491 :
1492 48 : return *relationship;
1493 48 : }
1494 :
1495 : /* Ensure this location has a "relationships" array (3.28.7). */
1496 :
1497 : json::array &
1498 48 : sarif_location::lazily_add_relationships_array ()
1499 : {
1500 48 : const char *const property_name = "relationships";
1501 48 : if (json::value *relationships = get (property_name))
1502 : {
1503 9 : gcc_assert (relationships->get_kind () == json::JSON_ARRAY);
1504 : return *static_cast <json::array *> (relationships);
1505 : }
1506 39 : json::array *relationships_arr = new json::array ();
1507 39 : set (property_name, relationships_arr);
1508 39 : return *relationships_arr;
1509 : }
1510 :
1511 : /* class sarif_ice_notification : public sarif_location_manager. */
1512 :
1513 : /* sarif_ice_notification's ctor.
1514 : DIAGNOSTIC is an internal compiler error. */
1515 :
1516 6 : sarif_ice_notification::
1517 : sarif_ice_notification (const diagnostic_info &diagnostic,
1518 : sarif_builder &builder,
1519 6 : std::unique_ptr<json::object> backtrace)
1520 : {
1521 : /* "locations" property (SARIF v2.1.0 section 3.58.4). */
1522 6 : auto locations_arr
1523 : = builder.make_locations_arr (*this,
1524 : diagnostic,
1525 6 : diagnostic_artifact_role::result_file);
1526 6 : set<json::array> ("locations", std::move (locations_arr));
1527 :
1528 : /* "message" property (SARIF v2.1.0 section 3.85.5). */
1529 6 : auto message_obj
1530 6 : = builder.make_message_object (pp_formatted_text (builder.get_printer ()));
1531 6 : pp_clear_output_area (builder.get_printer ());
1532 6 : set<sarif_message> ("message", std::move (message_obj));
1533 :
1534 : /* "level" property (SARIF v2.1.0 section 3.58.6). */
1535 6 : set_string ("level", "error");
1536 :
1537 : /* If we have backtrace information, add it as part of a property bag. */
1538 6 : if (backtrace)
1539 : {
1540 6 : sarif_property_bag &bag = get_or_create_properties ();
1541 6 : bag.set ("gcc/backtrace", std::move (backtrace));
1542 : }
1543 6 : }
1544 :
1545 : /* Implementation of sarif_location_manager::add_related_location vfunc
1546 : for notifications. */
1547 :
1548 : void
1549 1 : sarif_ice_notification::
1550 : add_related_location (std::unique_ptr<sarif_location> location_obj,
1551 : sarif_builder &builder)
1552 : {
1553 : /* Support for related locations within a notification was added
1554 : in SARIF 2.2; see https://github.com/oasis-tcs/sarif-spec/issues/540 */
1555 1 : if (builder.get_version () >= sarif_version::v2_2_prerelease_2024_08_08)
1556 1 : sarif_location_manager::add_related_location (std::move (location_obj),
1557 : builder);
1558 : /* Otherwise implicitly discard LOCATION_OBJ. */
1559 1 : }
1560 :
1561 : /* class sarif_location_relationship : public sarif_object. */
1562 :
1563 48 : sarif_location_relationship::
1564 : sarif_location_relationship (sarif_location &target,
1565 48 : sarif_location_manager &loc_mgr)
1566 48 : : m_kinds ((unsigned)location_relationship_kind::NUM_KINDS)
1567 : {
1568 48 : bitmap_clear (m_kinds);
1569 48 : set_integer ("target", target.lazily_add_id (loc_mgr));
1570 48 : }
1571 :
1572 : long
1573 0 : sarif_location_relationship::get_target_id () const
1574 : {
1575 0 : json::value *id = get ("id");
1576 0 : gcc_assert (id);
1577 0 : return static_cast <json::integer_number *> (id)->get ();
1578 : }
1579 :
1580 : void
1581 48 : sarif_location_relationship::
1582 : lazily_add_kind (enum location_relationship_kind kind)
1583 : {
1584 48 : if (bitmap_bit_p (m_kinds, (int)kind))
1585 : return; // already have this kind
1586 48 : bitmap_set_bit (m_kinds, (int)kind);
1587 :
1588 : // 3.34.3 kinds property
1589 48 : json::array *kinds_arr = nullptr;
1590 48 : if (json::value *kinds_val = get ("kinds"))
1591 : {
1592 0 : gcc_assert (kinds_val->get_kind () == json::JSON_ARRAY);
1593 : }
1594 : else
1595 : {
1596 48 : kinds_arr = new json::array ();
1597 48 : set ("kinds", kinds_arr);
1598 : }
1599 48 : const char *kind_str = get_string_for_location_relationship_kind (kind);
1600 48 : kinds_arr->append_string (kind_str);
1601 : }
1602 :
1603 : /* class sarif_code_flow : public sarif_object. */
1604 :
1605 34 : sarif_code_flow::sarif_code_flow (sarif_result &parent,
1606 34 : unsigned idx_within_parent)
1607 34 : : m_parent (parent),
1608 34 : m_idx_within_parent (idx_within_parent)
1609 : {
1610 : /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */
1611 34 : auto thread_flows_arr = std::make_unique<json::array> ();
1612 34 : m_thread_flows_arr = thread_flows_arr.get (); // borrowed
1613 34 : set<json::array> ("threadFlows", std::move (thread_flows_arr));
1614 34 : }
1615 :
1616 : sarif_thread_flow &
1617 164 : sarif_code_flow::get_or_append_thread_flow (const paths::thread &thread,
1618 : paths::thread_id_t thread_id)
1619 : {
1620 164 : sarif_thread_flow **slot = m_thread_id_map.get (thread_id);
1621 164 : if (slot)
1622 129 : return **slot;
1623 :
1624 35 : unsigned next_thread_flow_idx = m_thread_flows_arr->size ();
1625 35 : auto thread_flow_obj
1626 35 : = std::make_unique<sarif_thread_flow> (*this, thread, next_thread_flow_idx);
1627 35 : m_thread_id_map.put (thread_id, thread_flow_obj.get ()); // borrowed
1628 35 : sarif_thread_flow *result = thread_flow_obj.get ();
1629 35 : m_thread_flows_arr->append<sarif_thread_flow> (std::move (thread_flow_obj));
1630 35 : return *result;
1631 35 : }
1632 :
1633 : sarif_thread_flow &
1634 0 : sarif_code_flow::get_thread_flow (paths::thread_id_t thread_id)
1635 : {
1636 0 : sarif_thread_flow **slot = m_thread_id_map.get (thread_id);
1637 0 : gcc_assert (slot); // it must already have one
1638 0 : return **slot;
1639 : }
1640 :
1641 : void
1642 164 : sarif_code_flow::add_location (sarif_thread_flow_location &tfl_obj)
1643 : {
1644 164 : m_all_tfl_objs.push_back (&tfl_obj);
1645 164 : }
1646 :
1647 : sarif_thread_flow_location &
1648 181 : sarif_code_flow::get_thread_flow_loc_obj (paths::event_id_t event_id) const
1649 : {
1650 181 : gcc_assert (event_id.known_p ());
1651 181 : gcc_assert ((size_t)event_id.zero_based () < m_all_tfl_objs.size ());
1652 181 : sarif_thread_flow_location *tfl_obj = m_all_tfl_objs[event_id.zero_based ()];
1653 181 : gcc_assert (tfl_obj);
1654 181 : return *tfl_obj;
1655 : }
1656 :
1657 : /* class sarif_thread_flow : public sarif_object. */
1658 :
1659 35 : sarif_thread_flow::sarif_thread_flow (sarif_code_flow &parent,
1660 : const paths::thread &thread,
1661 35 : unsigned idx_within_parent)
1662 35 : : m_parent (parent),
1663 35 : m_idx_within_parent (idx_within_parent)
1664 : {
1665 : /* "id" property (SARIF v2.1.0 section 3.37.2). */
1666 35 : label_text name (thread.get_name (false));
1667 35 : set_string ("id", name.get ());
1668 :
1669 : /* "locations" property (SARIF v2.1.0 section 3.37.6). */
1670 35 : m_locations_arr = new json::array ();
1671 :
1672 : /* Give ownership of m_locations_arr to json::object;
1673 : keep a borrowed ptr. */
1674 35 : set ("locations", m_locations_arr);
1675 35 : }
1676 :
1677 : /* Add a sarif_thread_flow_location to this threadFlow object, but
1678 : don't populate it yet. */
1679 :
1680 : sarif_thread_flow_location &
1681 164 : sarif_thread_flow::add_location ()
1682 : {
1683 164 : const unsigned thread_flow_location_idx = m_locations_arr->size ();
1684 164 : sarif_thread_flow_location *thread_flow_loc_obj
1685 164 : = new sarif_thread_flow_location (*this, thread_flow_location_idx);
1686 164 : m_locations_arr->append (thread_flow_loc_obj);
1687 164 : m_parent.add_location (*thread_flow_loc_obj);
1688 164 : return *thread_flow_loc_obj;
1689 : }
1690 :
1691 : /* class sarif_builder. */
1692 :
1693 : /* sarif_builder's ctor. */
1694 :
1695 418 : sarif_builder::sarif_builder (diagnostics::context &dc,
1696 : pretty_printer &printer,
1697 : const line_maps *line_maps,
1698 : std::unique_ptr<sarif_serialization_format> serialization_format,
1699 418 : const sarif_generation_options &sarif_gen_opts)
1700 418 : : m_context (dc),
1701 418 : m_printer (&printer),
1702 418 : m_line_maps (line_maps),
1703 418 : m_token_printer (*this),
1704 418 : m_invocation_obj
1705 : (std::make_unique<sarif_invocation> (*this,
1706 418 : dc.get_original_argv ())),
1707 418 : m_results_array (new json::array ()),
1708 418 : m_cur_group_result (nullptr),
1709 418 : m_seen_any_relative_paths (false),
1710 418 : m_rule_id_set (),
1711 418 : m_rules_arr (new json::array ()),
1712 418 : m_cached_logical_locs
1713 : (std::make_unique<sarif_array_of_unique<sarif_logical_location>> ()),
1714 418 : m_run_graphs
1715 : (std::make_unique<sarif_array_of_unique<sarif_graph>> ()),
1716 418 : m_tabstop (dc.get_column_options ().m_tabstop),
1717 418 : m_serialization_format (std::move (serialization_format)),
1718 418 : m_sarif_gen_opts (sarif_gen_opts),
1719 418 : m_next_result_idx (0),
1720 836 : m_current_code_flow (nullptr)
1721 : {
1722 418 : gcc_assert (m_line_maps);
1723 418 : gcc_assert (m_serialization_format);
1724 418 : }
1725 :
1726 418 : sarif_builder::~sarif_builder ()
1727 : {
1728 : /* Normally m_filename_to_artifact_map will have been emptied as part
1729 : of make_run_object, but this isn't run by all the selftests.
1730 : Ensure the artifact objects are cleaned up for such cases. */
1731 972 : for (auto iter : m_filename_to_artifact_map)
1732 : {
1733 136 : sarif_artifact *artifact_obj = iter.second;
1734 136 : delete artifact_obj;
1735 : }
1736 418 : }
1737 :
1738 : void
1739 0 : sarif_builder::dump (FILE *out, int indent) const
1740 : {
1741 0 : dumping::emit_heading (out, indent, "serialization format");
1742 0 : m_serialization_format->dump (out, indent + 2);
1743 0 : dumping::emit_heading (out, indent, "SARIF generation options");
1744 0 : m_sarif_gen_opts.dump (out, indent + 2);
1745 0 : }
1746 :
1747 : /* Functions at which to stop the backtrace print. It's not
1748 : particularly helpful to print the callers of these functions. */
1749 :
1750 : static const char * const bt_stop[] =
1751 : {
1752 : "main",
1753 : "toplev::main",
1754 : "execute_one_pass",
1755 : "compile_file",
1756 : };
1757 :
1758 : struct bt_closure
1759 : {
1760 6 : bt_closure (sarif_builder &builder,
1761 : json::array *frames_arr)
1762 6 : : m_builder (builder),
1763 6 : m_frames_arr (frames_arr)
1764 : {
1765 : }
1766 :
1767 : sarif_builder &m_builder;
1768 : json::array *m_frames_arr;
1769 : };
1770 :
1771 : /* A callback function passed to the backtrace_full function. */
1772 :
1773 : static int
1774 22 : bt_callback (void *data, uintptr_t pc, const char *filename, int lineno,
1775 : const char *function)
1776 : {
1777 22 : bt_closure *closure = (bt_closure *)data;
1778 :
1779 : /* If we don't have any useful information, don't print
1780 : anything. */
1781 22 : if (filename == nullptr && function == nullptr)
1782 : return 0;
1783 :
1784 : /* Skip functions in context.cc or diagnostic-global-context.cc. */
1785 20 : if (closure->m_frames_arr->size () == 0
1786 12 : && filename != nullptr
1787 20 : && (strcmp (lbasename (filename), "context.cc") == 0
1788 12 : || strcmp (lbasename (filename),
1789 : "diagnostic-global-context.cc") == 0))
1790 6 : return 0;
1791 :
1792 : /* Print up to 20 functions. We could make this a --param, but
1793 : since this is only for debugging just use a constant for now. */
1794 14 : if (closure->m_frames_arr->size () >= 20)
1795 : {
1796 : /* Returning a non-zero value stops the backtrace. */
1797 : return 1;
1798 : }
1799 :
1800 14 : char *alc = nullptr;
1801 14 : if (function != nullptr)
1802 : {
1803 14 : char *str = cplus_demangle_v3 (function,
1804 : (DMGL_VERBOSE | DMGL_ANSI
1805 : | DMGL_GNU_V3 | DMGL_PARAMS));
1806 14 : if (str != nullptr)
1807 : {
1808 12 : alc = str;
1809 12 : function = str;
1810 : }
1811 :
1812 58 : for (size_t i = 0; i < ARRAY_SIZE (bt_stop); ++i)
1813 : {
1814 50 : size_t len = strlen (bt_stop[i]);
1815 50 : if (strncmp (function, bt_stop[i], len) == 0
1816 6 : && (function[len] == '\0' || function[len] == '('))
1817 : {
1818 6 : if (alc != nullptr)
1819 6 : free (alc);
1820 : /* Returning a non-zero value stops the backtrace. */
1821 6 : return 1;
1822 : }
1823 : }
1824 : }
1825 :
1826 8 : auto frame_obj = std::make_unique<json::object> ();
1827 :
1828 : /* I tried using sarifStack and sarifStackFrame for this
1829 : but it's not a good fit e.g. PC information. */
1830 8 : char buf[128];
1831 8 : snprintf (buf, sizeof (buf) - 1, "0x%lx", (unsigned long)pc);
1832 8 : frame_obj->set_string ("pc", buf);
1833 8 : if (function)
1834 8 : frame_obj->set_string ("function", function);
1835 8 : if (filename)
1836 8 : frame_obj->set_string ("filename", filename);
1837 8 : frame_obj->set_integer ("lineno", lineno);
1838 8 : closure->m_frames_arr->append (std::move (frame_obj));
1839 :
1840 8 : if (alc != nullptr)
1841 6 : free (alc);
1842 :
1843 8 : return 0;
1844 8 : }
1845 :
1846 : /* Attempt to generate a JSON object representing a backtrace,
1847 : for adding to ICE notifications. */
1848 :
1849 : std::unique_ptr<json::object>
1850 6 : sarif_builder::make_stack_from_backtrace ()
1851 : {
1852 6 : auto frames_arr = std::make_unique<json::array> ();
1853 :
1854 6 : backtrace_state *state = nullptr;
1855 6 : state = backtrace_create_state (nullptr, 0, nullptr, nullptr);
1856 6 : bt_closure closure (*this, frames_arr.get ());
1857 6 : const int frames_to_skip = 5;
1858 6 : if (state != nullptr)
1859 6 : backtrace_full (state, frames_to_skip, bt_callback, nullptr,
1860 : (void *) &closure);
1861 :
1862 6 : if (frames_arr->size () == 0)
1863 0 : return nullptr;
1864 :
1865 6 : auto stack = std::make_unique<json::object> ();
1866 6 : stack->set ("frames", std::move (frames_arr));
1867 6 : return stack;
1868 6 : }
1869 :
1870 : void
1871 290 : sarif_builder::set_main_input_filename (const char *name)
1872 : {
1873 : /* Mark NAME as the artifact that the tool was instructed to scan.
1874 : Only quote the contents if it gets referenced by physical locations,
1875 : since otherwise the "no diagnostics" case would quote the main input
1876 : file, and doing so noticeably bloated the output seen in analyzer
1877 : integration testing (build directory went from 20G -> 21G). */
1878 290 : if (name)
1879 290 : get_or_create_artifact (name,
1880 : diagnostic_artifact_role::analysis_target,
1881 : false);
1882 290 : }
1883 :
1884 : /* Implementation of "on_report_diagnostic" for SARIF output. */
1885 :
1886 : void
1887 602 : sarif_builder::on_report_diagnostic (const diagnostic_info &diagnostic,
1888 : enum kind orig_diag_kind,
1889 : sarif_sink_buffer *buffer)
1890 : {
1891 602 : pp_output_formatted_text (m_printer, m_context.get_urlifier ());
1892 :
1893 602 : if (diagnostic.m_kind == kind::ice || diagnostic.m_kind == kind::ice_nobt)
1894 : {
1895 6 : std::unique_ptr<json::object> stack = make_stack_from_backtrace ();
1896 6 : m_invocation_obj->add_notification_for_ice (diagnostic, *this,
1897 : std::move (stack));
1898 :
1899 : /* Print a header for the remaining output to stderr, and
1900 : return, attempting to print the usual ICE messages to
1901 : stderr. Hopefully this will be helpful to the user in
1902 : indicating what's gone wrong (also for DejaGnu, for pruning
1903 : those messages). */
1904 6 : fnotice (stderr, "Internal compiler error:\n");
1905 :
1906 6 : return;
1907 6 : }
1908 :
1909 596 : if (buffer)
1910 : {
1911 : /* When buffering, we can only handle top-level results. */
1912 21 : gcc_assert (!m_cur_group_result);
1913 21 : buffer->add_result (make_result_object (diagnostic, orig_diag_kind,
1914 21 : m_next_result_idx++));
1915 21 : return;
1916 : }
1917 :
1918 575 : if (m_cur_group_result)
1919 : /* Nested diagnostic. */
1920 117 : m_cur_group_result->on_nested_diagnostic (diagnostic,
1921 : orig_diag_kind,
1922 : *this);
1923 : else
1924 : {
1925 : /* Top-level diagnostic. */
1926 458 : m_cur_group_result = make_result_object (diagnostic, orig_diag_kind,
1927 458 : m_next_result_idx++);
1928 : }
1929 : }
1930 :
1931 : /* Implementation of diagnostics::context::m_diagrams.m_emission_cb
1932 : for SARIF output. */
1933 :
1934 : void
1935 4 : sarif_builder::emit_diagram (const diagram &d)
1936 : {
1937 : /* We must be within the emission of a top-level diagnostic. */
1938 4 : gcc_assert (m_cur_group_result);
1939 4 : m_cur_group_result->on_diagram (d, *this);
1940 4 : }
1941 :
1942 : /* Implementation of "end_group_cb" for SARIF output.
1943 : Append the current sarifResult to results, and set it to nullptr. */
1944 :
1945 : void
1946 483 : sarif_builder::end_group ()
1947 : {
1948 483 : if (m_cur_group_result)
1949 : {
1950 458 : m_cur_group_result->process_worklist (*this);
1951 458 : m_results_array->append<sarif_result> (std::move (m_cur_group_result));
1952 : }
1953 483 : }
1954 :
1955 : void
1956 1 : sarif_builder::
1957 : report_global_digraph (const lazily_created<digraphs::digraph> &ldg)
1958 : {
1959 1 : auto &dg = ldg.get_or_create ();
1960 :
1961 : /* Presumably the location manager must be nullptr; see
1962 : https://github.com/oasis-tcs/sarif-spec/issues/712 */
1963 1 : m_run_graphs->append (make_sarif_graph (dg, this, nullptr));
1964 1 : }
1965 :
1966 : void
1967 45 : sarif_builder::
1968 : report_digraph_for_logical_location (const lazily_created<digraphs::digraph> &ldg,
1969 : logical_locations::key logical_loc)
1970 : {
1971 : /* Adding the graph to the logical location itself would break consolidation
1972 : of logical locations, as the objects would no longer be equal.
1973 : So we add the graph to the per-run graphs, but add a logicalLocation
1974 : property to it. */
1975 :
1976 45 : auto &dg = ldg.get_or_create ();
1977 :
1978 : /* Presumably the location manager must be nullptr; see
1979 : https://github.com/oasis-tcs/sarif-spec/issues/712 */
1980 45 : auto graph_obj = make_sarif_graph (dg, this, nullptr);
1981 :
1982 45 : auto &bag = graph_obj->get_or_create_properties ();
1983 90 : bag.set ("logicalLocation",
1984 45 : make_minimal_sarif_logical_location (logical_loc));
1985 :
1986 45 : m_run_graphs->append (std::move (graph_obj));
1987 45 : }
1988 :
1989 : /* Create a top-level object, and add it to all the results
1990 : (and other entities) we've seen so far, moving ownership
1991 : to the object. */
1992 :
1993 : std::unique_ptr<sarif_log>
1994 282 : sarif_builder::flush_to_object ()
1995 : {
1996 282 : DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_context.get_logger (),
1997 282 : "diagnostics::sarif_builder::flush_to_object");
1998 282 : m_invocation_obj->prepare_to_flush (*this);
1999 282 : std::unique_ptr<sarif_log> top
2000 282 : = make_top_level_object (std::move (m_invocation_obj),
2001 282 : std::move (m_results_array));
2002 282 : return top;
2003 282 : }
2004 :
2005 : /* Create a top-level object, and add it to all the results
2006 : (and other entities) we've seen so far.
2007 :
2008 : Flush it all to OUTF. */
2009 :
2010 : void
2011 114 : sarif_builder::flush_to_file (FILE *outf)
2012 : {
2013 114 : DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_context.get_logger (),
2014 114 : "diagnostics::sarif_builder::flush_to_file");
2015 114 : std::unique_ptr<sarif_log> top = flush_to_object ();
2016 114 : m_serialization_format->write_to_file (outf, *top);
2017 114 : }
2018 :
2019 : /* Attempt to convert DIAG_KIND to a suitable value for the "level"
2020 : property (SARIF v2.1.0 section 3.27.10).
2021 :
2022 : Return nullptr if there isn't one. */
2023 :
2024 : static const char *
2025 479 : maybe_get_sarif_level (enum kind diag_kind)
2026 : {
2027 479 : switch (diag_kind)
2028 : {
2029 : case kind::warning:
2030 : return "warning";
2031 238 : case kind::fatal:
2032 238 : case kind::error:
2033 238 : return "error";
2034 8 : case kind::note:
2035 8 : case kind::anachronism:
2036 8 : return "note";
2037 0 : default:
2038 0 : return nullptr;
2039 : }
2040 : }
2041 :
2042 : /* Make a string for DIAG_KIND suitable for use a ruleId
2043 : (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't
2044 : have anything better to use. */
2045 :
2046 : static char *
2047 238 : make_rule_id_for_diagnostic_kind (enum kind diag_kind)
2048 : {
2049 : /* Lose the trailing ": ". */
2050 238 : const char *kind_text = get_text_for_kind (diag_kind);
2051 238 : size_t len = strlen (kind_text);
2052 238 : gcc_assert (len > 2);
2053 238 : gcc_assert (kind_text[len - 2] == ':');
2054 238 : gcc_assert (kind_text[len - 1] == ' ');
2055 238 : char *rstrip = xstrdup (kind_text);
2056 238 : rstrip[len - 2] = '\0';
2057 238 : return rstrip;
2058 : }
2059 :
2060 : /* Make a "result" object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */
2061 :
2062 : std::unique_ptr<sarif_result>
2063 479 : sarif_builder::make_result_object (const diagnostic_info &diagnostic,
2064 : enum kind orig_diag_kind,
2065 : unsigned idx_within_parent)
2066 : {
2067 479 : auto result_obj = std::make_unique<sarif_result> (idx_within_parent);
2068 :
2069 : /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */
2070 : /* Ideally we'd have an option_name for these. */
2071 479 : label_text option_text
2072 479 : = m_context.get_option_name (diagnostic.m_option_id,
2073 479 : orig_diag_kind, diagnostic.m_kind);
2074 479 : if (option_text.get ())
2075 : {
2076 : /* Lazily create reportingDescriptor objects for and add to m_rules_arr.
2077 : Set ruleId referencing them. */
2078 241 : result_obj->set_string ("ruleId", option_text.get ());
2079 241 : if (m_rule_id_set.find (option_text.get ()) == m_rule_id_set.end ())
2080 : {
2081 : /* This is the first time we've seen this ruleId. */
2082 : /* Add to set, taking ownership. */
2083 67 : m_rule_id_set.insert (option_text.get ());
2084 :
2085 67 : m_rules_arr->append<sarif_reporting_descriptor>
2086 67 : (make_reporting_descriptor_object_for_warning (diagnostic,
2087 : orig_diag_kind,
2088 : option_text.get ()));
2089 : }
2090 : }
2091 : else
2092 : {
2093 : /* Otherwise, we have an "error" or a stray "note"; use the
2094 : diagnostic kind as the ruleId, so that the result object at least
2095 : has a ruleId.
2096 : We don't bother creating reportingDescriptor objects for these. */
2097 238 : char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind);
2098 238 : result_obj->set_string ("ruleId", rule_id);
2099 238 : free (rule_id);
2100 : }
2101 :
2102 479 : if (diagnostic.m_metadata)
2103 : {
2104 : /* "taxa" property (SARIF v2.1.0 section 3.27.8). */
2105 35 : if (int cwe_id = diagnostic.m_metadata->get_cwe ())
2106 : {
2107 25 : auto taxa_arr = std::make_unique<json::array> ();
2108 25 : taxa_arr->append<sarif_reporting_descriptor_reference>
2109 25 : (make_reporting_descriptor_reference_object_for_cwe_id (cwe_id));
2110 25 : result_obj->set<json::array> ("taxa", std::move (taxa_arr));
2111 25 : }
2112 :
2113 35 : diagnostic.m_metadata->maybe_add_sarif_properties (*result_obj);
2114 :
2115 : /* We don't yet support diagnostics::metadata::rule. */
2116 : }
2117 :
2118 : /* "level" property (SARIF v2.1.0 section 3.27.10). */
2119 479 : if (const char *sarif_level = maybe_get_sarif_level (diagnostic.m_kind))
2120 479 : result_obj->set_string ("level", sarif_level);
2121 :
2122 : /* "message" property (SARIF v2.1.0 section 3.27.11). */
2123 479 : auto message_obj
2124 479 : = make_message_object (pp_formatted_text (m_printer));
2125 479 : pp_clear_output_area (m_printer);
2126 479 : result_obj->set<sarif_message> ("message", std::move (message_obj));
2127 :
2128 : /* "locations" property (SARIF v2.1.0 section 3.27.12). */
2129 479 : result_obj->set<json::array>
2130 958 : ("locations",
2131 479 : make_locations_arr (*result_obj.get (),
2132 : diagnostic,
2133 : diagnostic_artifact_role::result_file));
2134 :
2135 : /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */
2136 479 : if (const paths::path *path = diagnostic.m_richloc->get_path ())
2137 : {
2138 34 : auto code_flows_arr = std::make_unique<json::array> ();
2139 34 : const unsigned code_flow_index = 0;
2140 34 : code_flows_arr->append<sarif_code_flow>
2141 34 : (make_code_flow_object (*result_obj.get (),
2142 : code_flow_index,
2143 : *path));
2144 34 : result_obj->set<json::array> ("codeFlows", std::move (code_flows_arr));
2145 34 : }
2146 :
2147 : // "graphs" property (SARIF v2.1.0 section 3.27.19). */
2148 479 : if (diagnostic.m_metadata)
2149 35 : if (auto ldg = diagnostic.m_metadata->get_lazy_digraphs ())
2150 : {
2151 1 : auto &digraphs = ldg->get_or_create ();
2152 1 : auto graphs_arr = std::make_unique<json::array> ();
2153 3 : for (auto &iter : digraphs)
2154 2 : graphs_arr->append (make_sarif_graph (*iter, this,
2155 2 : result_obj.get ()));
2156 1 : if (graphs_arr->size () > 0)
2157 1 : result_obj->set<json::array> ("graphs", std::move (graphs_arr));
2158 1 : }
2159 :
2160 : /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is
2161 : set up later, if any nested diagnostics occur within this diagnostic
2162 : group. */
2163 :
2164 : /* "fixes" property (SARIF v2.1.0 section 3.27.30). */
2165 479 : const rich_location *richloc = diagnostic.m_richloc;
2166 479 : if (richloc->get_num_fixit_hints ())
2167 : {
2168 9 : auto fix_arr = std::make_unique<json::array> ();
2169 9 : fix_arr->append<sarif_fix> (make_fix_object (*richloc));
2170 9 : result_obj->set<json::array> ("fixes", std::move (fix_arr));
2171 9 : }
2172 :
2173 958 : return result_obj;
2174 479 : }
2175 :
2176 : /* Make a "reportingDescriptor" object (SARIF v2.1.0 section 3.49)
2177 : for a GCC warning. */
2178 :
2179 : std::unique_ptr<sarif_reporting_descriptor>
2180 67 : sarif_builder::
2181 : make_reporting_descriptor_object_for_warning (const diagnostic_info &diagnostic,
2182 : enum kind /*orig_diag_kind*/,
2183 : const char *option_text)
2184 : {
2185 67 : auto reporting_desc = std::make_unique<sarif_reporting_descriptor> ();
2186 :
2187 : /* "id" property (SARIF v2.1.0 section 3.49.3). */
2188 67 : reporting_desc->set_string ("id", option_text);
2189 :
2190 : /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since
2191 : it seems redundant compared to "id". */
2192 :
2193 : /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
2194 67 : label_text option_url = m_context.get_option_url (diagnostic.m_option_id);
2195 67 : if (option_url.get ())
2196 67 : reporting_desc->set_string ("helpUri", option_url.get ());
2197 :
2198 67 : return reporting_desc;
2199 67 : }
2200 :
2201 : /* Make a "reportingDescriptor" object (SARIF v2.1.0 section 3.49)
2202 : for CWE_ID, for use within the CWE taxa array. */
2203 :
2204 : std::unique_ptr<sarif_reporting_descriptor>
2205 22 : sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const
2206 : {
2207 22 : auto reporting_desc = std::make_unique<sarif_reporting_descriptor> ();
2208 :
2209 : /* "id" property (SARIF v2.1.0 section 3.49.3). */
2210 22 : {
2211 22 : pretty_printer pp;
2212 22 : pp_printf (&pp, "%i", cwe_id);
2213 22 : reporting_desc->set_string ("id", pp_formatted_text (&pp));
2214 22 : }
2215 :
2216 : /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */
2217 22 : {
2218 22 : char *url = get_cwe_url (cwe_id);
2219 22 : reporting_desc->set_string ("helpUri", url);
2220 22 : free (url);
2221 : }
2222 :
2223 22 : return reporting_desc;
2224 : }
2225 :
2226 : /* Make a "reportingDescriptorReference" object (SARIF v2.1.0 section 3.52)
2227 : referencing CWE_ID, for use within a result object.
2228 : Also, add CWE_ID to m_cwe_id_set. */
2229 :
2230 : std::unique_ptr<sarif_reporting_descriptor_reference>
2231 25 : sarif_builder::
2232 : make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id)
2233 : {
2234 25 : auto desc_ref_obj = std::make_unique<sarif_reporting_descriptor_reference> ();
2235 :
2236 : /* "id" property (SARIF v2.1.0 section 3.52.4). */
2237 25 : {
2238 25 : pretty_printer pp;
2239 25 : pp_printf (&pp, "%i", cwe_id);
2240 25 : desc_ref_obj->set_string ("id", pp_formatted_text (&pp));
2241 25 : }
2242 :
2243 : /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */
2244 25 : desc_ref_obj->set<sarif_tool_component_reference>
2245 25 : ("toolComponent", make_tool_component_reference_object_for_cwe ());
2246 :
2247 : /* Add CWE_ID to our set. */
2248 25 : gcc_assert (cwe_id > 0);
2249 25 : m_cwe_id_set.add (cwe_id);
2250 :
2251 25 : return desc_ref_obj;
2252 : }
2253 :
2254 : /* Make a "toolComponentReference" object (SARIF v2.1.0 section 3.54) that
2255 : references the CWE taxonomy. */
2256 :
2257 : std::unique_ptr<sarif_tool_component_reference>
2258 25 : sarif_builder::
2259 : make_tool_component_reference_object_for_cwe () const
2260 : {
2261 25 : auto comp_ref_obj = std::make_unique<sarif_tool_component_reference> ();
2262 :
2263 : /* "name" property (SARIF v2.1.0 section 3.54.3). */
2264 25 : comp_ref_obj->set_string ("name", "cwe");
2265 :
2266 25 : return comp_ref_obj;
2267 : }
2268 :
2269 : /* Make an array suitable for use as the "locations" property of:
2270 : - a "result" object (SARIF v2.1.0 section 3.27.12), or
2271 : - a "notification" object (SARIF v2.1.0 section 3.58.4).
2272 : Use LOC_MGR for any locations that need "id" values. */
2273 :
2274 : std::unique_ptr<json::array>
2275 485 : sarif_builder::make_locations_arr (sarif_location_manager &loc_mgr,
2276 : const diagnostic_info &diagnostic,
2277 : enum diagnostic_artifact_role role)
2278 : {
2279 485 : auto locations_arr = std::make_unique<json::array> ();
2280 485 : logical_locations::key logical_loc;
2281 485 : if (auto client_data_hooks = m_context.get_client_data_hooks ())
2282 293 : logical_loc = client_data_hooks->get_current_logical_location ();
2283 :
2284 485 : auto location_obj
2285 485 : = make_location_object (&loc_mgr, *diagnostic.m_richloc, logical_loc, role);
2286 : /* Don't add entirely empty location objects to the array. */
2287 485 : if (!location_obj->is_empty ())
2288 421 : locations_arr->append<sarif_location> (std::move (location_obj));
2289 :
2290 970 : return locations_arr;
2291 485 : }
2292 :
2293 : /* If LOGICAL_LOC is non-null, use it to create a "logicalLocations" property
2294 : within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4) with a minimal logical
2295 : location object referencing theRuns.logicalLocations (3.33.3). */
2296 :
2297 : void
2298 932 : sarif_builder::
2299 : set_any_logical_locs_arr (sarif_location &location_obj,
2300 : logical_locations::key logical_loc)
2301 : {
2302 932 : if (!logical_loc)
2303 658 : return;
2304 274 : auto logical_loc_mgr = get_logical_location_manager ();
2305 274 : gcc_assert (logical_loc_mgr);
2306 274 : auto location_locs_arr = std::make_unique<json::array> ();
2307 :
2308 274 : auto logical_loc_obj = make_minimal_sarif_logical_location (logical_loc);
2309 :
2310 274 : location_locs_arr->append<sarif_logical_location>
2311 274 : (std::move (logical_loc_obj));
2312 :
2313 274 : location_obj.set<json::array> ("logicalLocations",
2314 : std::move (location_locs_arr));
2315 274 : }
2316 :
2317 : /* Make a "location" object (SARIF v2.1.0 section 3.28) for RICH_LOC
2318 : and LOGICAL_LOC.
2319 : Use LOC_MGR for any locations that need "id" values, and for
2320 : any worklist items.
2321 : Note that we might not always have a LOC_MGR; see
2322 : https://github.com/oasis-tcs/sarif-spec/issues/712 */
2323 :
2324 : std::unique_ptr<sarif_location>
2325 768 : sarif_builder::make_location_object (sarif_location_manager *loc_mgr,
2326 : const rich_location &rich_loc,
2327 : logical_locations::key logical_loc,
2328 : enum diagnostic_artifact_role role)
2329 : {
2330 1536 : class escape_nonascii_renderer : public content_renderer
2331 : {
2332 : public:
2333 768 : escape_nonascii_renderer (const rich_location &richloc,
2334 : enum diagnostics_escape_format escape_format)
2335 768 : : m_richloc (richloc),
2336 768 : m_escape_format (escape_format)
2337 : {}
2338 :
2339 : std::unique_ptr<sarif_multiformat_message_string>
2340 136 : render (const sarif_builder &builder) const final override
2341 : {
2342 136 : diagnostics::context dc;
2343 136 : diagnostic_initialize (&dc, 0);
2344 136 : auto &source_printing_opts = dc.get_source_printing_options ();
2345 136 : source_printing_opts.enabled = true;
2346 136 : source_printing_opts.colorize_source_p = false;
2347 136 : source_printing_opts.show_labels_p = true;
2348 136 : source_printing_opts.show_line_numbers_p = true;
2349 :
2350 136 : rich_location my_rich_loc (m_richloc);
2351 136 : my_rich_loc.set_escape_on_output (true);
2352 :
2353 136 : source_print_policy source_policy (dc);
2354 136 : dc.set_escape_format (m_escape_format);
2355 136 : text_sink text_output (dc);
2356 136 : source_policy.print (*text_output.get_printer (),
2357 : my_rich_loc, kind::error, nullptr);
2358 :
2359 136 : const char *buf = pp_formatted_text (text_output.get_printer ());
2360 136 : std::unique_ptr<sarif_multiformat_message_string> result
2361 136 : = builder.make_multiformat_message_string (buf);
2362 :
2363 136 : diagnostic_finish (&dc);
2364 :
2365 272 : return result;
2366 136 : }
2367 : private:
2368 : const rich_location &m_richloc;
2369 : enum diagnostics_escape_format m_escape_format;
2370 : } the_renderer (rich_loc,
2371 768 : m_context.get_escape_format ());
2372 :
2373 768 : auto location_obj = std::make_unique<sarif_location> ();
2374 :
2375 : /* Get primary loc from RICH_LOC. */
2376 768 : location_t loc = rich_loc.get_loc ();
2377 :
2378 : /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
2379 768 : const content_renderer *snippet_renderer
2380 768 : = rich_loc.escape_on_output_p () ? &the_renderer : nullptr;
2381 768 : if (auto phs_loc_obj
2382 : = maybe_make_physical_location_object (loc, role,
2383 : rich_loc.get_column_override (),
2384 768 : snippet_renderer))
2385 666 : location_obj->set<sarif_physical_location> ("physicalLocation",
2386 768 : std::move (phs_loc_obj));
2387 :
2388 : /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
2389 768 : set_any_logical_locs_arr (*location_obj, logical_loc);
2390 :
2391 : /* Handle labelled ranges and/or secondary locations. */
2392 768 : {
2393 768 : std::unique_ptr<json::array> annotations_arr = nullptr;
2394 1814 : for (unsigned int i = 0; i < rich_loc.get_num_locations (); i++)
2395 : {
2396 1046 : const location_range *range = rich_loc.get_range (i);
2397 1046 : bool handled = false;
2398 1046 : if (const range_label *label = range->m_label)
2399 : {
2400 410 : label_text text = label->get_text (i);
2401 410 : if (text.get ())
2402 : {
2403 : /* Create annotations for any labelled ranges. */
2404 410 : location_t range_loc = rich_loc.get_loc (i);
2405 410 : auto region
2406 : = maybe_make_region_object (range_loc,
2407 410 : rich_loc.get_column_override ());
2408 410 : if (region)
2409 : {
2410 410 : if (!annotations_arr)
2411 137 : annotations_arr = std::make_unique<json::array> ();
2412 410 : region->set<sarif_message>
2413 410 : ("message", make_message_object (text.get ()));
2414 410 : annotations_arr->append<sarif_region> (std::move (region));
2415 410 : handled = true;
2416 : }
2417 410 : }
2418 410 : }
2419 :
2420 : /* Add related locations for any secondary locations in RICH_LOC
2421 : that don't have labels (and thus aren't added to "annotations"). */
2422 1046 : if (loc_mgr && i > 0 && !handled)
2423 4 : loc_mgr->add_relationship_to_worklist
2424 4 : (*location_obj.get (),
2425 : sarif_location_manager::worklist_item::kind::unlabelled_secondary_location,
2426 4 : range->m_loc);
2427 : }
2428 768 : if (annotations_arr)
2429 : /* "annotations" property (SARIF v2.1.0 section 3.28.6). */
2430 137 : location_obj->set<json::array> ("annotations",
2431 : std::move (annotations_arr));
2432 768 : }
2433 :
2434 768 : if (loc_mgr)
2435 768 : add_any_include_chain (*loc_mgr, *location_obj.get (), loc);
2436 :
2437 : /* A flag for hinting that the diagnostic involves issues at the
2438 : level of character encodings (such as homoglyphs, or misleading
2439 : bidirectional control codes), and thus that it will be helpful
2440 : to the user if we show some representation of
2441 : how the characters in the pertinent source lines are encoded. */
2442 768 : if (rich_loc.escape_on_output_p ())
2443 : {
2444 136 : sarif_property_bag &bag = location_obj->get_or_create_properties ();
2445 136 : bag.set_bool ("gcc/escapeNonAscii", rich_loc.escape_on_output_p ());
2446 : }
2447 :
2448 768 : return location_obj;
2449 768 : }
2450 :
2451 : /* If WHERE was #included from somewhere, add a worklist item
2452 : to LOC_MGR to lazily add a location for the #include location,
2453 : and relationships between it and the LOCATION_OBJ.
2454 : Compare with diagnostics::context::report_current_module, but rather
2455 : than iterating the current chain, we add the next edge and iterate
2456 : in the worklist, so that edges are only added once. */
2457 :
2458 : void
2459 953 : sarif_builder::add_any_include_chain (sarif_location_manager &loc_mgr,
2460 : sarif_location &location_obj,
2461 : location_t where)
2462 : {
2463 953 : if (where <= BUILTINS_LOCATION)
2464 930 : return;
2465 :
2466 851 : const line_map_ordinary *map = nullptr;
2467 851 : linemap_resolve_location (m_line_maps, where,
2468 : LRK_MACRO_DEFINITION_LOCATION,
2469 : &map);
2470 :
2471 851 : if (!map)
2472 : return;
2473 :
2474 851 : location_t include_loc = linemap_included_from (map);
2475 851 : map = linemap_included_from_linemap (m_line_maps, map);
2476 851 : if (!map)
2477 : return;
2478 23 : loc_mgr.add_relationship_to_worklist
2479 23 : (location_obj,
2480 : sarif_result::worklist_item::kind::included_from,
2481 : include_loc);
2482 : }
2483 :
2484 : /* Make a "location" object (SARIF v2.1.0 section 3.28) for WHERE
2485 : within an include chain. */
2486 :
2487 : std::unique_ptr<sarif_location>
2488 21 : sarif_builder::make_location_object (sarif_location_manager &loc_mgr,
2489 : location_t loc,
2490 : enum diagnostic_artifact_role role)
2491 : {
2492 21 : auto location_obj = std::make_unique<sarif_location> ();
2493 :
2494 : /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
2495 21 : if (auto phs_loc_obj
2496 21 : = maybe_make_physical_location_object (loc, role, 0, nullptr))
2497 21 : location_obj->set<sarif_physical_location> ("physicalLocation",
2498 21 : std::move (phs_loc_obj));
2499 :
2500 21 : add_any_include_chain (loc_mgr, *location_obj.get (), loc);
2501 :
2502 21 : return location_obj;
2503 : }
2504 :
2505 : /* Make a "location" object (SARIF v2.1.0 section 3.28) for EVENT
2506 : within a paths::path. */
2507 :
2508 : std::unique_ptr<sarif_location>
2509 164 : sarif_builder::make_location_object (sarif_location_manager &loc_mgr,
2510 : const paths::event &event,
2511 : enum diagnostic_artifact_role role)
2512 : {
2513 164 : auto location_obj = std::make_unique<sarif_location> ();
2514 :
2515 : /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */
2516 164 : location_t loc = event.get_location ();
2517 164 : if (auto phs_loc_obj
2518 164 : = maybe_make_physical_location_object (loc, role, 0, nullptr))
2519 164 : location_obj->set<sarif_physical_location> ("physicalLocation",
2520 164 : std::move (phs_loc_obj));
2521 :
2522 : /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */
2523 164 : logical_locations::key logical_loc = event.get_logical_location ();
2524 164 : set_any_logical_locs_arr (*location_obj, logical_loc);
2525 :
2526 : /* "message" property (SARIF v2.1.0 section 3.28.5). */
2527 164 : std::unique_ptr<pretty_printer> pp = get_printer ()->clone ();
2528 164 : event.print_desc (*pp);
2529 164 : location_obj->set<sarif_message>
2530 328 : ("message",
2531 164 : make_message_object (pp_formatted_text (pp.get ())));
2532 :
2533 164 : add_any_include_chain (loc_mgr, *location_obj.get (), loc);
2534 :
2535 328 : return location_obj;
2536 164 : }
2537 :
2538 : /* Make a "physicalLocation" object (SARIF v2.1.0 section 3.29) for LOC.
2539 :
2540 : If COLUMN_OVERRIDE is non-zero, then use it as the column number
2541 : if LOC has no column information.
2542 :
2543 : Ensure that we have an artifact object for the file, adding ROLE to it,
2544 : and flagging that we will attempt to embed the contents of the artifact
2545 : when writing it out. */
2546 :
2547 : std::unique_ptr<sarif_physical_location>
2548 953 : sarif_builder::
2549 : maybe_make_physical_location_object (location_t loc,
2550 : enum diagnostic_artifact_role role,
2551 : int column_override,
2552 : const content_renderer *snippet_renderer)
2553 : {
2554 953 : if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == nullptr)
2555 102 : return nullptr;
2556 :
2557 851 : auto phys_loc_obj = std::make_unique<sarif_physical_location> ();
2558 :
2559 : /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */
2560 851 : phys_loc_obj->set<sarif_artifact_location>
2561 851 : ("artifactLocation", make_artifact_location_object (loc));
2562 851 : get_or_create_artifact (LOCATION_FILE (loc), role, true);
2563 :
2564 : /* "region" property (SARIF v2.1.0 section 3.29.4). */
2565 851 : if (auto region_obj = maybe_make_region_object (loc, column_override))
2566 851 : phys_loc_obj->set<sarif_region> ("region", std::move (region_obj));
2567 :
2568 : /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */
2569 851 : if (auto context_region_obj
2570 851 : = maybe_make_region_object_for_context (loc, snippet_renderer))
2571 847 : phys_loc_obj->set<sarif_region> ("contextRegion",
2572 851 : std::move (context_region_obj));
2573 :
2574 : /* Instead, we add artifacts to the run as a whole,
2575 : with artifact.contents.
2576 : Could do both, though. */
2577 :
2578 851 : return phys_loc_obj;
2579 851 : }
2580 :
2581 : /* Make an "artifactLocation" object (SARIF v2.1.0 section 3.4) for LOC,
2582 : or return nullptr. */
2583 :
2584 : std::unique_ptr<sarif_artifact_location>
2585 860 : sarif_builder::make_artifact_location_object (location_t loc)
2586 : {
2587 860 : return make_artifact_location_object (LOCATION_FILE (loc));
2588 : }
2589 :
2590 : /* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4)
2591 : for when we need to express paths relative to PWD. */
2592 :
2593 : #define PWD_PROPERTY_NAME ("PWD")
2594 :
2595 : /* Make an "artifactLocation" object (SARIF v2.1.0 section 3.4) for FILENAME,
2596 : or return nullptr. */
2597 :
2598 : std::unique_ptr<sarif_artifact_location>
2599 1714 : sarif_builder::make_artifact_location_object (const char *filename)
2600 : {
2601 1714 : auto artifact_loc_obj = std::make_unique<sarif_artifact_location> ();
2602 :
2603 : /* "uri" property (SARIF v2.1.0 section 3.4.3). */
2604 1714 : artifact_loc_obj->set_string ("uri", filename);
2605 :
2606 1714 : if (filename[0] != '/')
2607 : {
2608 : /* If we have a relative path, set the "uriBaseId" property
2609 : (SARIF v2.1.0 section 3.4.4). */
2610 56 : artifact_loc_obj->set_string ("uriBaseId", PWD_PROPERTY_NAME);
2611 56 : m_seen_any_relative_paths = true;
2612 : }
2613 :
2614 1714 : return artifact_loc_obj;
2615 : }
2616 :
2617 : /* Get the PWD, or nullptr, as an absolute file-based URI,
2618 : adding a trailing forward slash (as required by SARIF v2.1.0
2619 : section 3.14.14). */
2620 :
2621 : static char *
2622 44 : make_pwd_uri_str ()
2623 : {
2624 : /* The prefix of a file-based URI, up to, but not including the path. */
2625 : #define FILE_PREFIX ("file://")
2626 :
2627 44 : const char *pwd = getpwd ();
2628 44 : if (!pwd)
2629 : return nullptr;
2630 44 : size_t len = strlen (pwd);
2631 44 : if (len == 0 || pwd[len - 1] != '/')
2632 44 : return concat (FILE_PREFIX, pwd, "/", nullptr);
2633 : else
2634 : {
2635 0 : gcc_assert (pwd[len - 1] == '/');
2636 0 : return concat (FILE_PREFIX, pwd, nullptr);
2637 : }
2638 : }
2639 :
2640 : /* Make an "artifactLocation" object (SARIF v2.1.0 section 3.4) for the pwd,
2641 : for use in the "run.originalUriBaseIds" property (SARIF v2.1.0
2642 : section 3.14.14) when we have any relative paths. */
2643 :
2644 : std::unique_ptr<sarif_artifact_location>
2645 44 : sarif_builder::make_artifact_location_object_for_pwd () const
2646 : {
2647 44 : auto artifact_loc_obj = std::make_unique<sarif_artifact_location> ();
2648 :
2649 : /* "uri" property (SARIF v2.1.0 section 3.4.3). */
2650 44 : if (char *pwd = make_pwd_uri_str ())
2651 : {
2652 44 : gcc_assert (strlen (pwd) > 0);
2653 44 : gcc_assert (pwd[strlen (pwd) - 1] == '/');
2654 44 : artifact_loc_obj->set_string ("uri", pwd);
2655 44 : free (pwd);
2656 : }
2657 :
2658 44 : return artifact_loc_obj;
2659 : }
2660 :
2661 : /* Get the column number within EXPLOC. */
2662 :
2663 : int
2664 2330 : sarif_builder::get_sarif_column (expanded_location exploc) const
2665 : {
2666 2330 : cpp_char_column_policy policy (m_tabstop, cpp_wcwidth);
2667 2330 : return location_compute_display_column (m_context.get_file_cache (),
2668 2330 : exploc, policy);
2669 : }
2670 :
2671 : /* Make a "region" object (SARIF v2.1.0 section 3.30) for LOC,
2672 : or return nullptr.
2673 :
2674 : If COLUMN_OVERRIDE is non-zero, then use it as the column number
2675 : if LOC has no column information.
2676 :
2677 : We only support text properties of regions ("text regions"),
2678 : not binary properties ("binary regions"); see 3.30.1. */
2679 :
2680 : std::unique_ptr<sarif_region>
2681 1261 : sarif_builder::maybe_make_region_object (location_t loc,
2682 : int column_override) const
2683 : {
2684 1261 : location_t caret_loc = get_pure_location (loc);
2685 :
2686 1261 : if (caret_loc <= BUILTINS_LOCATION)
2687 0 : return nullptr;
2688 :
2689 1261 : location_t start_loc = get_start (loc);
2690 1261 : location_t finish_loc = get_finish (loc);
2691 :
2692 1261 : expanded_location exploc_caret = expand_location (caret_loc);
2693 1261 : expanded_location exploc_start = expand_location (start_loc);
2694 1261 : expanded_location exploc_finish = expand_location (finish_loc);
2695 :
2696 1261 : if (exploc_start.file !=exploc_caret.file)
2697 0 : return nullptr;
2698 1261 : if (exploc_finish.file !=exploc_caret.file)
2699 0 : return nullptr;
2700 :
2701 : /* We can have line == 0 in the presence of "#" lines.
2702 : SARIF requires lines > 0, so if we hit this case we don't have a
2703 : way of validly representing the region as SARIF; bail out. */
2704 1261 : if (exploc_start.line <= 0)
2705 4 : return nullptr;
2706 :
2707 1257 : auto region_obj = std::make_unique<sarif_region> ();
2708 :
2709 : /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
2710 1257 : region_obj->set_integer ("startLine", exploc_start.line);
2711 :
2712 : /* "startColumn" property (SARIF v2.1.0 section 3.30.6).
2713 :
2714 : We use column == 0 to mean the whole line, so omit the column
2715 : information for this case, unless COLUMN_OVERRIDE is non-zero,
2716 : (for handling certain awkward lexer diagnostics) */
2717 :
2718 1257 : if (exploc_start.column == 0 && column_override)
2719 : /* Use the provided column number. */
2720 : exploc_start.column = column_override;
2721 :
2722 1257 : if (exploc_start.column > 0)
2723 : {
2724 1240 : int start_column = get_sarif_column (exploc_start);
2725 1240 : region_obj->set_integer ("startColumn", start_column);
2726 : }
2727 :
2728 : /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
2729 1257 : if (exploc_finish.line != exploc_start.line
2730 0 : && exploc_finish.line > 0)
2731 0 : region_obj->set_integer ("endLine", exploc_finish.line);
2732 :
2733 : /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
2734 : This expresses the column immediately beyond the range.
2735 :
2736 : We use column == 0 to mean the whole line, so omit the column
2737 : information for this case. */
2738 1257 : if (exploc_finish.column > 0)
2739 : {
2740 1072 : int next_column = get_sarif_column (exploc_finish) + 1;
2741 1072 : region_obj->set_integer ("endColumn", next_column);
2742 : }
2743 :
2744 1257 : return region_obj;
2745 1257 : }
2746 :
2747 : /* Make a "region" object (SARIF v2.1.0 section 3.30) for the "contextRegion"
2748 : property (SARIF v2.1.0 section 3.29.5) of a "physicalLocation".
2749 :
2750 : This is similar to maybe_make_region_object, but ignores column numbers,
2751 : covering the line(s) as a whole, and including a "snippet" property
2752 : embedding those source lines, making it easier for consumers to show
2753 : the pertinent source. */
2754 :
2755 : std::unique_ptr<sarif_region>
2756 851 : sarif_builder::
2757 : maybe_make_region_object_for_context (location_t loc,
2758 : const content_renderer *snippet_renderer)
2759 : const
2760 : {
2761 851 : location_t caret_loc = get_pure_location (loc);
2762 :
2763 851 : if (caret_loc <= BUILTINS_LOCATION)
2764 0 : return nullptr;
2765 :
2766 851 : location_t start_loc = get_start (loc);
2767 851 : location_t finish_loc = get_finish (loc);
2768 :
2769 851 : expanded_location exploc_caret = expand_location (caret_loc);
2770 851 : expanded_location exploc_start = expand_location (start_loc);
2771 851 : expanded_location exploc_finish = expand_location (finish_loc);
2772 :
2773 851 : if (exploc_start.file !=exploc_caret.file)
2774 0 : return nullptr;
2775 851 : if (exploc_finish.file !=exploc_caret.file)
2776 0 : return nullptr;
2777 :
2778 : /* We can have line == 0 in the presence of "#" lines.
2779 : SARIF requires lines > 0, so if we hit this case we don't have a
2780 : way of validly representing the region as SARIF; bail out. */
2781 851 : if (exploc_start.line <= 0)
2782 4 : return nullptr;
2783 :
2784 847 : auto region_obj = std::make_unique<sarif_region> ();
2785 :
2786 : /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
2787 847 : region_obj->set_integer ("startLine", exploc_start.line);
2788 :
2789 : /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
2790 847 : if (exploc_finish.line != exploc_start.line
2791 0 : && exploc_finish.line > 0)
2792 0 : region_obj->set_integer ("endLine", exploc_finish.line);
2793 :
2794 : /* "snippet" property (SARIF v2.1.0 section 3.30.13). */
2795 847 : if (auto artifact_content_obj
2796 : = maybe_make_artifact_content_object (exploc_start.file,
2797 : exploc_start.line,
2798 : exploc_finish.line,
2799 847 : snippet_renderer))
2800 675 : region_obj->set<sarif_artifact_content> ("snippet",
2801 847 : std::move (artifact_content_obj));
2802 :
2803 847 : return region_obj;
2804 847 : }
2805 :
2806 : /* Make a "region" object (SARIF v2.1.0 section 3.30) for the deletion region
2807 : of HINT (as per SARIF v2.1.0 section 3.57.3). */
2808 :
2809 : std::unique_ptr<sarif_region>
2810 9 : sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const
2811 : {
2812 9 : location_t start_loc = hint.get_start_loc ();
2813 9 : location_t next_loc = hint.get_next_loc ();
2814 :
2815 9 : expanded_location exploc_start = expand_location (start_loc);
2816 9 : expanded_location exploc_next = expand_location (next_loc);
2817 :
2818 9 : auto region_obj = std::make_unique<sarif_region> ();
2819 :
2820 : /* "startLine" property (SARIF v2.1.0 section 3.30.5) */
2821 9 : region_obj->set_integer ("startLine", exploc_start.line);
2822 :
2823 : /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */
2824 9 : int start_col = get_sarif_column (exploc_start);
2825 9 : region_obj->set_integer ("startColumn", start_col);
2826 :
2827 : /* "endLine" property (SARIF v2.1.0 section 3.30.7) */
2828 9 : if (exploc_next.line != exploc_start.line)
2829 0 : region_obj->set_integer ("endLine", exploc_next.line);
2830 :
2831 : /* "endColumn" property (SARIF v2.1.0 section 3.30.8).
2832 : This expresses the column immediately beyond the range. */
2833 9 : int next_col = get_sarif_column (exploc_next);
2834 9 : region_obj->set_integer ("endColumn", next_col);
2835 :
2836 9 : return region_obj;
2837 : }
2838 :
2839 : /* Attempt to get a string for a logicalLocation's "kind" property
2840 : (SARIF v2.1.0 section 3.33.7).
2841 : Return nullptr if unknown. */
2842 :
2843 : static const char *
2844 554 : maybe_get_sarif_kind (enum logical_locations::kind kind)
2845 : {
2846 554 : using namespace logical_locations;
2847 :
2848 554 : switch (kind)
2849 : {
2850 0 : default:
2851 0 : gcc_unreachable ();
2852 : case logical_locations::kind::unknown:
2853 : return nullptr;
2854 :
2855 : /* Kinds within executable code. */
2856 319 : case logical_locations::kind::function:
2857 319 : return "function";
2858 0 : case logical_locations::kind::member:
2859 0 : return "member";
2860 0 : case logical_locations::kind::module_:
2861 0 : return "module";
2862 217 : case logical_locations::kind::namespace_:
2863 217 : return "namespace";
2864 18 : case logical_locations::kind::type:
2865 18 : return "type";
2866 0 : case logical_locations::kind::return_type:
2867 0 : return "returnType";
2868 0 : case logical_locations::kind::parameter:
2869 0 : return "parameter";
2870 0 : case logical_locations::kind::variable:
2871 0 : return "variable";
2872 :
2873 : /* Kinds within XML or HTML documents. */
2874 0 : case logical_locations::kind::element:
2875 0 : return "element";
2876 0 : case logical_locations::kind::attribute:
2877 0 : return "attribute";
2878 0 : case logical_locations::kind::text:
2879 0 : return "text";
2880 0 : case logical_locations::kind::comment:
2881 0 : return "comment";
2882 0 : case logical_locations::kind::processing_instruction:
2883 0 : return "processingInstruction";
2884 0 : case logical_locations::kind::dtd:
2885 0 : return "dtd";
2886 0 : case logical_locations::kind::declaration:
2887 0 : return "declaration";
2888 :
2889 : /* Kinds within JSON documents. */
2890 0 : case logical_locations::kind::object:
2891 0 : return "object";
2892 0 : case logical_locations::kind::array:
2893 0 : return "array";
2894 0 : case logical_locations::kind::property:
2895 0 : return "property";
2896 0 : case logical_locations::kind::value:
2897 0 : return "value";
2898 : }
2899 : }
2900 :
2901 : /* Set PROPERTY_NAME within this bag to a "logicalLocation" object (SARIF v2.1.0
2902 : section 3.33) for LOGICAL_LOC. The object has an "index" property to refer to
2903 : theRuns.logicalLocations (3.33.3). */
2904 :
2905 : void
2906 0 : sarif_property_bag::set_logical_location (const char *property_name,
2907 : sarif_builder &builder,
2908 : logical_locations::key logical_loc)
2909 : {
2910 0 : set<sarif_logical_location>
2911 0 : (property_name,
2912 0 : builder.make_minimal_sarif_logical_location (logical_loc));
2913 0 : }
2914 :
2915 : static void
2916 2021 : copy_any_property_bag (const digraphs::object &input_obj,
2917 : sarif_object &output_obj)
2918 : {
2919 2021 : if (input_obj.get_property_bag ())
2920 : {
2921 1162 : const json::object &old_bag = *input_obj.get_property_bag ();
2922 1162 : sarif_property_bag &new_bag = output_obj.get_or_create_properties ();
2923 4251 : for (size_t i = 0; i < old_bag.get_num_keys (); ++i)
2924 : {
2925 3089 : const char *key = old_bag.get_key (i);
2926 3089 : json::value *val = old_bag.get (key);
2927 3089 : new_bag.set (key, val->clone ());
2928 : }
2929 : }
2930 2021 : }
2931 :
2932 : std::unique_ptr<sarif_graph>
2933 83 : make_sarif_graph (const digraphs::digraph &g,
2934 : sarif_builder *builder,
2935 : sarif_location_manager *sarif_location_mgr)
2936 : {
2937 83 : auto result = std::make_unique<sarif_graph> ();
2938 :
2939 : // 3.39.2 description property
2940 83 : if (const char *desc = g.get_description ())
2941 52 : if (builder)
2942 48 : result->set<sarif_message> ("description",
2943 48 : builder->make_message_object (desc));
2944 :
2945 83 : copy_any_property_bag (g, *result);
2946 :
2947 : // 3.39.3 nodes property
2948 83 : auto nodes_arr = std::make_unique<json::array> ();
2949 83 : const int num_nodes = g.get_num_nodes ();
2950 214 : for (int i = 0; i < num_nodes; ++i)
2951 131 : nodes_arr->append (make_sarif_node (g.get_node (i),
2952 : builder,
2953 : sarif_location_mgr));
2954 83 : result->set ("nodes", std::move (nodes_arr));
2955 :
2956 : // 3.39.4 edges property
2957 83 : auto edges_arr = std::make_unique<json::array> ();
2958 83 : const int num_edges = g.get_num_edges ();
2959 797 : for (int i = 0; i < num_edges; ++i)
2960 714 : edges_arr->append (make_sarif_edge (g.get_edge (i), builder));
2961 83 : result->set ("edges", std::move (edges_arr));
2962 :
2963 166 : return result;
2964 83 : }
2965 :
2966 : std::unique_ptr<sarif_node>
2967 1224 : make_sarif_node (const digraphs::node &n,
2968 : sarif_builder *builder,
2969 : sarif_location_manager *sarif_location_mgr)
2970 : {
2971 1224 : auto result = std::make_unique<sarif_node> ();
2972 :
2973 : // 3.40.2 id property
2974 2448 : result->set_string ("id", n.get_id ().c_str ());
2975 :
2976 1224 : copy_any_property_bag (n, *result);
2977 :
2978 : // 3.40.3 label property
2979 1224 : if (const char *label = n.get_label ())
2980 406 : if (builder)
2981 402 : result->set<sarif_message> ("label",
2982 402 : builder->make_message_object (label));
2983 :
2984 : // 3.40.4 location property
2985 1224 : if (n.get_logical_loc ()
2986 1224 : || n.get_physical_loc () != UNKNOWN_LOCATION)
2987 38 : if (builder)
2988 : {
2989 38 : rich_location rich_loc
2990 38 : (line_table, n.get_physical_loc ());
2991 38 : auto loc_obj
2992 : = builder->make_location_object
2993 : (sarif_location_mgr,
2994 : rich_loc,
2995 : n.get_logical_loc (),
2996 38 : diagnostic_artifact_role::scanned_file);
2997 38 : result->set<sarif_location> ("location",
2998 : std::move (loc_obj));
2999 38 : }
3000 :
3001 : // 3.40.5 children property
3002 1224 : if (const int num_children = n.get_num_children ())
3003 : {
3004 301 : auto children_arr = std::make_unique<json::array> ();
3005 1394 : for (int i = 0; i < num_children; ++i)
3006 1093 : children_arr->append (make_sarif_node (n.get_child (i),
3007 : builder,
3008 : sarif_location_mgr));
3009 301 : result->set ("children", std::move (children_arr));
3010 301 : }
3011 :
3012 1224 : return result;
3013 : }
3014 :
3015 : std::unique_ptr<sarif_edge>
3016 714 : make_sarif_edge (const digraphs::edge &e,
3017 : sarif_builder *builder)
3018 : {
3019 714 : auto result = std::make_unique<sarif_edge> ();
3020 :
3021 : // 3.41.2 id property
3022 1428 : result->set_string ("id", e.get_id ().c_str ());
3023 :
3024 714 : copy_any_property_bag (e, *result);
3025 :
3026 : // 3.41.3 label property
3027 714 : if (const char *label = e.get_label ())
3028 396 : if (builder)
3029 392 : result->set<sarif_message> ("label",
3030 392 : builder->make_message_object (label));
3031 :
3032 : // 3.41.4 sourceNodeId property
3033 1428 : result->set_string ("sourceNodeId", e.get_src_node ().get_id ().c_str ());
3034 :
3035 : // 3.41.5 targetNodeId property
3036 1428 : result->set_string ("targetNodeId", e.get_dst_node ().get_id ().c_str ());
3037 :
3038 714 : return result;
3039 : }
3040 :
3041 : void
3042 27 : sarif_property_bag::set_graph (const char *property_name,
3043 : sarif_builder &builder,
3044 : sarif_location_manager *sarif_location_mgr,
3045 : const digraphs::digraph &g)
3046 : {
3047 54 : set<sarif_graph> (property_name,
3048 27 : make_sarif_graph (g, &builder, sarif_location_mgr));
3049 27 : }
3050 :
3051 : /* Ensure that m_cached_logical_locs has a "logicalLocation" object
3052 : (SARIF v2.1.0 section 3.33) for K, and return its index within the
3053 : array. */
3054 :
3055 : int
3056 554 : sarif_builder::
3057 : ensure_sarif_logical_location_for (logical_locations::key k)
3058 : {
3059 554 : auto logical_loc_mgr = get_logical_location_manager ();
3060 554 : gcc_assert (logical_loc_mgr);
3061 :
3062 554 : auto sarif_logical_loc = std::make_unique<sarif_logical_location> ();
3063 :
3064 554 : label_text short_name = logical_loc_mgr->get_short_name (k);
3065 554 : if (short_name.get ())
3066 554 : sarif_logical_loc->set_string ("name", short_name.get ());
3067 :
3068 : /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
3069 554 : label_text name_with_scope = logical_loc_mgr->get_name_with_scope (k);
3070 554 : if (name_with_scope.get ())
3071 536 : sarif_logical_loc->set_string ("fullyQualifiedName",
3072 : name_with_scope.get ());
3073 :
3074 : /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */
3075 554 : label_text internal_name = logical_loc_mgr->get_internal_name (k);
3076 554 : if (internal_name.get ())
3077 319 : sarif_logical_loc->set_string ("decoratedName", internal_name.get ());
3078 :
3079 : /* "kind" property (SARIF v2.1.0 section 3.33.7). */
3080 554 : enum logical_locations::kind kind = logical_loc_mgr->get_kind (k);
3081 554 : if (const char *sarif_kind_str = maybe_get_sarif_kind (kind))
3082 554 : sarif_logical_loc->set_string ("kind", sarif_kind_str);
3083 :
3084 : /* "parentIndex" property (SARIF v2.1.0 section 3.33.8). */
3085 554 : if (auto parent_key = logical_loc_mgr->get_parent (k))
3086 : {
3087 : /* Recurse upwards. */
3088 235 : int parent_index = ensure_sarif_logical_location_for (parent_key);
3089 235 : sarif_logical_loc->set_integer ("parentIndex", parent_index);
3090 : }
3091 :
3092 : /* Consolidate if this logical location already exists. */
3093 554 : int index
3094 554 : = m_cached_logical_locs->append_uniquely (std::move (sarif_logical_loc));
3095 :
3096 554 : return index;
3097 554 : }
3098 :
3099 : /* Ensure that theRuns.logicalLocations (3.14.17) has a "logicalLocation" object
3100 : (SARIF v2.1.0 section 3.33) for LOGICAL_LOC.
3101 : Create and return a minimal logicalLocation object referring to the
3102 : full object by index. */
3103 :
3104 : std::unique_ptr<sarif_logical_location>
3105 319 : sarif_builder::
3106 : make_minimal_sarif_logical_location (logical_locations::key logical_loc)
3107 : {
3108 319 : auto logical_loc_mgr = get_logical_location_manager ();
3109 319 : gcc_assert (logical_loc_mgr);
3110 :
3111 : /* Ensure that m_cached_logical_locs has a "logicalLocation" object
3112 : (SARIF v2.1.0 section 3.33) for LOGICAL_LOC, and return its index within
3113 : the array. */
3114 :
3115 319 : auto sarif_logical_loc = std::make_unique <sarif_logical_location> ();
3116 :
3117 319 : int index = ensure_sarif_logical_location_for (logical_loc);
3118 :
3119 : // 3.33.3 index property
3120 319 : sarif_logical_loc->set_integer ("index", index);
3121 :
3122 : /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */
3123 319 : label_text name_with_scope
3124 319 : = logical_loc_mgr->get_name_with_scope (logical_loc);
3125 319 : if (name_with_scope.get ())
3126 319 : sarif_logical_loc->set_string ("fullyQualifiedName",
3127 : name_with_scope.get ());
3128 :
3129 319 : return sarif_logical_loc;
3130 319 : }
3131 :
3132 : label_text
3133 114 : make_sarif_url_for_event (const sarif_code_flow *code_flow,
3134 : paths::event_id_t event_id)
3135 : {
3136 114 : gcc_assert (event_id.known_p ());
3137 :
3138 114 : if (!code_flow)
3139 97 : return label_text ();
3140 :
3141 17 : const sarif_thread_flow_location &tfl_obj
3142 17 : = code_flow->get_thread_flow_loc_obj (event_id);
3143 17 : const int location_idx = tfl_obj.get_index_within_parent ();
3144 :
3145 17 : const sarif_thread_flow &thread_flow_obj = tfl_obj.get_parent ();
3146 17 : const int thread_flow_idx = thread_flow_obj.get_index_within_parent ();
3147 :
3148 17 : const sarif_code_flow &code_flow_obj = thread_flow_obj.get_parent ();
3149 17 : const int code_flow_idx = code_flow_obj.get_index_within_parent ();
3150 :
3151 17 : const sarif_result &result_obj = code_flow_obj.get_parent ();
3152 17 : const int result_idx = result_obj.get_index_within_parent ();
3153 :
3154 : /* We only support a single run object in the log. */
3155 17 : const int run_idx = 0;
3156 :
3157 17 : char *buf = xasprintf
3158 17 : ("sarif:/runs/%i/results/%i/codeFlows/%i/threadFlows/%i/locations/%i",
3159 : run_idx, result_idx, code_flow_idx, thread_flow_idx, location_idx);
3160 17 : return label_text::take (buf);
3161 : }
3162 :
3163 : /* Make a "codeFlow" object (SARIF v2.1.0 section 3.36) for PATH. */
3164 :
3165 : std::unique_ptr<sarif_code_flow>
3166 34 : sarif_builder::make_code_flow_object (sarif_result &result,
3167 : unsigned idx_within_parent,
3168 : const paths::path &path)
3169 : {
3170 34 : auto code_flow_obj
3171 34 : = std::make_unique <sarif_code_flow> (result, idx_within_parent);
3172 :
3173 : /* First pass:
3174 : Create threadFlows and threadFlowLocation objects within them,
3175 : effectively recording a mapping from event_id to threadFlowLocation
3176 : so that we can later go from an event_id to a URI within the
3177 : SARIF file. */
3178 198 : for (unsigned i = 0; i < path.num_events (); i++)
3179 : {
3180 164 : const paths::event &event = path.get_event (i);
3181 164 : const paths::thread_id_t thread_id = event.get_thread_id ();
3182 :
3183 164 : sarif_thread_flow &thread_flow_obj
3184 164 : = code_flow_obj->get_or_append_thread_flow (path.get_thread (thread_id),
3185 : thread_id);
3186 164 : thread_flow_obj.add_location ();
3187 : }
3188 :
3189 : /* Second pass: walk the events, populating the tfl objs. */
3190 34 : m_current_code_flow = code_flow_obj.get ();
3191 198 : for (unsigned i = 0; i < path.num_events (); i++)
3192 : {
3193 164 : const paths::event &event = path.get_event (i);
3194 164 : sarif_thread_flow_location &thread_flow_loc_obj
3195 164 : = code_flow_obj->get_thread_flow_loc_obj (i);
3196 164 : populate_thread_flow_location_object (result,
3197 : thread_flow_loc_obj,
3198 : event,
3199 : i);
3200 : }
3201 34 : m_current_code_flow = nullptr;
3202 :
3203 34 : return code_flow_obj;
3204 : }
3205 :
3206 : /* Populate TFL_OBJ, a "threadFlowLocation" object (SARIF v2.1.0 section 3.38)
3207 : based on EVENT. */
3208 :
3209 : void
3210 164 : sarif_builder::
3211 : populate_thread_flow_location_object (sarif_result &result,
3212 : sarif_thread_flow_location &tfl_obj,
3213 : const paths::event &ev,
3214 : int event_execution_idx)
3215 : {
3216 : /* Give paths::event subclasses a chance to add custom properties
3217 : via a property bag. */
3218 164 : ev.maybe_add_sarif_properties (*this, tfl_obj);
3219 :
3220 164 : if (get_opts ().m_state_graph)
3221 27 : if (auto state_graph = ev.maybe_make_diagnostic_state_graph (true))
3222 : {
3223 27 : sarif_property_bag &props = tfl_obj.get_or_create_properties ();
3224 :
3225 : #define PROPERTY_PREFIX "gcc/diagnostics/paths/event/"
3226 27 : props.set_graph (PROPERTY_PREFIX "state_graph",
3227 : *this,
3228 : /* Use RESULT for any related locations in the graph's
3229 : nodes.
3230 : It's not clear if this is correct; see:
3231 : https://github.com/oasis-tcs/sarif-spec/issues/712
3232 : */
3233 : &result,
3234 27 : *state_graph);
3235 : #undef PROPERTY_PREFIX
3236 27 : }
3237 :
3238 : /* "location" property (SARIF v2.1.0 section 3.38.3). */
3239 164 : tfl_obj.set<sarif_location>
3240 328 : ("location",
3241 164 : make_location_object (result, ev, diagnostic_artifact_role::traced_file));
3242 :
3243 : /* "kinds" property (SARIF v2.1.0 section 3.38.8). */
3244 164 : paths::event::meaning m = ev.get_meaning ();
3245 164 : if (auto kinds_arr = maybe_make_kinds_array (m))
3246 164 : tfl_obj.set<json::array> ("kinds", std::move (kinds_arr));
3247 :
3248 : /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */
3249 164 : tfl_obj.set_integer ("nestingLevel", ev.get_stack_depth ());
3250 :
3251 : /* "executionOrder" property (SARIF v2.1.0 3.38.11).
3252 : Offset by 1 to match the human-readable values emitted by %@. */
3253 164 : tfl_obj.set_integer ("executionOrder", event_execution_idx + 1);
3254 :
3255 : /* It might be nice to eventually implement the following for -fanalyzer:
3256 : - the "stack" property (SARIF v2.1.0 section 3.38.5)
3257 : - the "state" property (SARIF v2.1.0 section 3.38.9)
3258 : - the "importance" property (SARIF v2.1.0 section 3.38.13). */
3259 164 : }
3260 :
3261 : /* If M has any known meaning, make a json array suitable for the "kinds"
3262 : property of a "threadFlowLocation" object (SARIF v2.1.0 section 3.38.8).
3263 :
3264 : Otherwise, return nullptr. */
3265 :
3266 : std::unique_ptr<json::array>
3267 164 : sarif_builder::
3268 : maybe_make_kinds_array (paths::event::meaning m) const
3269 : {
3270 164 : using namespace paths;
3271 :
3272 164 : if (m.m_verb == event::verb::unknown
3273 22 : && m.m_noun == event::noun::unknown
3274 22 : && m.m_property == event::property::unknown)
3275 22 : return nullptr;
3276 :
3277 142 : auto kinds_arr = std::make_unique<json::array> ();
3278 284 : if (const char *verb_str
3279 142 : = event::meaning::maybe_get_verb_str (m.m_verb))
3280 142 : kinds_arr->append_string (verb_str);
3281 284 : if (const char *noun_str
3282 142 : = event::meaning::maybe_get_noun_str (m.m_noun))
3283 62 : kinds_arr->append_string (noun_str);
3284 284 : if (const char *property_str
3285 142 : = event::meaning::maybe_get_property_str (m.m_property))
3286 30 : kinds_arr->append_string (property_str);
3287 142 : return kinds_arr;
3288 142 : }
3289 :
3290 : /* In "3.11.5 Messages with placeholders":
3291 : "Within both plain text and formatted message strings, the characters
3292 : "{" and "}" SHALL be represented by the character sequences
3293 : "{{" and "}}" respectively." */
3294 :
3295 : static std::string
3296 2193 : escape_braces (const char *text)
3297 : {
3298 2193 : std::string result;
3299 72322 : while (char ch = *text++)
3300 70129 : switch (ch)
3301 : {
3302 36 : case '{':
3303 36 : case '}':
3304 36 : result += ch;
3305 : /* Fall through. */
3306 70129 : default:
3307 70129 : result += ch;
3308 70129 : break;
3309 : }
3310 2193 : return result;
3311 : }
3312 :
3313 : static void
3314 2193 : set_string_property_escaping_braces (json::object &obj,
3315 : const char *property_name,
3316 : const char *value)
3317 : {
3318 2193 : std::string escaped (escape_braces (value));
3319 2193 : obj.set_string (property_name, escaped.c_str ());
3320 2193 : }
3321 :
3322 : /* Make a "message" object (SARIF v2.1.0 section 3.11) for MSG. */
3323 :
3324 : std::unique_ptr<sarif_message>
3325 2027 : sarif_builder::make_message_object (const char *msg) const
3326 : {
3327 2027 : auto message_obj = std::make_unique<sarif_message> ();
3328 :
3329 : /* "text" property (SARIF v2.1.0 section 3.11.8). */
3330 2027 : set_string_property_escaping_braces (*message_obj,
3331 : "text", msg);
3332 :
3333 2027 : return message_obj;
3334 : }
3335 :
3336 : /* Make a "message" object (SARIF v2.1.0 section 3.11) for D.
3337 : We emit the diagram as a code block within the Markdown part
3338 : of the message. */
3339 :
3340 : std::unique_ptr<sarif_message>
3341 4 : sarif_builder::make_message_object_for_diagram (const diagram &d)
3342 : {
3343 4 : auto message_obj = std::make_unique<sarif_message> ();
3344 :
3345 : /* "text" property (SARIF v2.1.0 section 3.11.8). */
3346 4 : set_string_property_escaping_braces (*message_obj,
3347 : "text", d.get_alt_text ());
3348 :
3349 4 : pretty_printer *const pp = m_printer;
3350 4 : char *saved_prefix = pp_take_prefix (pp);
3351 4 : pp_set_prefix (pp, nullptr);
3352 :
3353 : /* "To produce a code block in Markdown, simply indent every line of
3354 : the block by at least 4 spaces or 1 tab."
3355 : Here we use 4 spaces. */
3356 4 : d.get_canvas ().print_to_pp (pp, " ");
3357 4 : pp_set_prefix (pp, saved_prefix);
3358 :
3359 : /* "markdown" property (SARIF v2.1.0 section 3.11.9). */
3360 4 : set_string_property_escaping_braces (*message_obj,
3361 : "markdown", pp_formatted_text (pp));
3362 :
3363 4 : pp_clear_output_area (pp);
3364 :
3365 4 : return message_obj;
3366 : }
3367 :
3368 : /* Make a "multiformatMessageString object" (SARIF v2.1.0 section 3.12)
3369 : for MSG. */
3370 :
3371 : std::unique_ptr<sarif_multiformat_message_string>
3372 158 : sarif_builder::make_multiformat_message_string (const char *msg) const
3373 : {
3374 158 : auto message_obj = std::make_unique<sarif_multiformat_message_string> ();
3375 :
3376 : /* "text" property (SARIF v2.1.0 section 3.12.3). */
3377 158 : set_string_property_escaping_braces (*message_obj,
3378 : "text", msg);
3379 :
3380 158 : return message_obj;
3381 : }
3382 :
3383 : /* Convert VERSION to a value for the "$schema" property
3384 : of a "sarifLog" object (SARIF v2.1.0 section 3.13.3). */
3385 :
3386 : static const char *
3387 290 : sarif_version_to_url (enum sarif_version version)
3388 : {
3389 290 : switch (version)
3390 : {
3391 0 : default:
3392 0 : gcc_unreachable ();
3393 : case sarif_version::v2_1_0:
3394 : return "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json";
3395 90 : case sarif_version::v2_2_prerelease_2024_08_08:
3396 90 : 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";
3397 : }
3398 : }
3399 :
3400 : /* Convert VERSION to a value for the "version" property
3401 : of a "sarifLog" object (SARIF v2.1.0 section 3.13.2). */
3402 :
3403 : static const char *
3404 290 : sarif_version_to_property (enum sarif_version version)
3405 : {
3406 290 : switch (version)
3407 : {
3408 0 : default:
3409 0 : gcc_unreachable ();
3410 : case sarif_version::v2_1_0:
3411 : return "2.1.0";
3412 90 : case sarif_version::v2_2_prerelease_2024_08_08:
3413 : /* I would have used "2.2-prerelease-2024-08-08",
3414 : but the schema only accepts "2.2". */
3415 90 : return "2.2";
3416 : }
3417 : }
3418 :
3419 : /* Make a top-level "sarifLog" object (SARIF v2.1.0 section 3.13). */
3420 :
3421 : std::unique_ptr<sarif_log>
3422 282 : sarif_builder::
3423 : make_top_level_object (std::unique_ptr<sarif_invocation> invocation_obj,
3424 : std::unique_ptr<json::array> results)
3425 : {
3426 282 : auto log_obj = std::make_unique<sarif_log> ();
3427 :
3428 : /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */
3429 282 : log_obj->set_string ("$schema", sarif_version_to_url (get_version ()));
3430 :
3431 : /* "version" property (SARIF v2.1.0 section 3.13.2). */
3432 282 : log_obj->set_string ("version", sarif_version_to_property (get_version ()));
3433 :
3434 : /* "runs" property (SARIF v2.1.0 section 3.13.4). */
3435 282 : auto run_arr = std::make_unique<json::array> ();
3436 282 : auto run_obj = make_run_object (std::move (invocation_obj),
3437 282 : std::move (results));
3438 282 : run_arr->append<sarif_run> (std::move (run_obj));
3439 282 : log_obj->set<json::array> ("runs", std::move (run_arr));
3440 :
3441 564 : return log_obj;
3442 282 : }
3443 :
3444 : /* Make a "run" object (SARIF v2.1.0 section 3.14). */
3445 :
3446 : std::unique_ptr<sarif_run>
3447 282 : sarif_builder::
3448 : make_run_object (std::unique_ptr<sarif_invocation> invocation_obj,
3449 : std::unique_ptr<json::array> results)
3450 : {
3451 282 : auto run_obj = std::make_unique<sarif_run> ();
3452 :
3453 : /* "tool" property (SARIF v2.1.0 section 3.14.6). */
3454 282 : run_obj->set<sarif_tool> ("tool", make_tool_object ());
3455 :
3456 : /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
3457 282 : if (auto taxonomies_arr = maybe_make_taxonomies_array ())
3458 282 : run_obj->set<json::array> ("taxonomies", std::move (taxonomies_arr));
3459 :
3460 : /* "invocations" property (SARIF v2.1.0 section 3.14.11). */
3461 282 : {
3462 282 : auto invocations_arr = std::make_unique<json::array> ();
3463 282 : invocations_arr->append (std::move (invocation_obj));
3464 282 : run_obj->set<json::array> ("invocations", std::move (invocations_arr));
3465 282 : }
3466 :
3467 : /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */
3468 282 : if (m_seen_any_relative_paths)
3469 : {
3470 44 : auto orig_uri_base_ids = std::make_unique<json::object> ();
3471 44 : orig_uri_base_ids->set<sarif_artifact_location>
3472 44 : (PWD_PROPERTY_NAME, make_artifact_location_object_for_pwd ());
3473 44 : run_obj->set<json::object> ("originalUriBaseIds",
3474 : std::move (orig_uri_base_ids));
3475 44 : }
3476 :
3477 : /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */
3478 282 : auto artifacts_arr = std::make_unique<json::array> ();
3479 864 : for (auto iter : m_filename_to_artifact_map)
3480 : {
3481 300 : sarif_artifact *artifact_obj = iter.second;
3482 300 : if (artifact_obj->embed_contents_p ())
3483 241 : artifact_obj->populate_contents (*this);
3484 300 : artifact_obj->populate_roles ();
3485 300 : artifacts_arr->append (artifact_obj);
3486 : }
3487 282 : run_obj->set<json::array> ("artifacts", std::move (artifacts_arr));
3488 282 : m_filename_to_artifact_map.empty ();
3489 :
3490 : /* "results" property (SARIF v2.1.0 section 3.14.23). */
3491 282 : run_obj->set<json::array> ("results", std::move (results));
3492 :
3493 : /* "logicalLocations" property (SARIF v2.1.0 3.14.17). */
3494 282 : if (m_cached_logical_locs->size () > 0)
3495 : {
3496 63 : m_cached_logical_locs->add_explicit_index_values ();
3497 63 : run_obj->set<json::array> ("logicalLocations",
3498 63 : std::move (m_cached_logical_locs));
3499 : }
3500 :
3501 : // "graphs" property (SARIF v2.1.0 3.14.20)
3502 282 : if (m_run_graphs->size () > 0)
3503 2 : run_obj->set<json::array> ("graphs",
3504 2 : std::move (m_run_graphs));
3505 :
3506 564 : return run_obj;
3507 282 : }
3508 :
3509 : /* Make a "tool" object (SARIF v2.1.0 section 3.18). */
3510 :
3511 : std::unique_ptr<sarif_tool>
3512 282 : sarif_builder::make_tool_object ()
3513 : {
3514 282 : auto tool_obj = std::make_unique<sarif_tool> ();
3515 :
3516 : /* "driver" property (SARIF v2.1.0 section 3.18.2). */
3517 282 : tool_obj->set<sarif_tool_component> ("driver",
3518 282 : make_driver_tool_component_object ());
3519 :
3520 : /* Report plugins via the "extensions" property
3521 : (SARIF v2.1.0 section 3.18.3). */
3522 282 : if (auto client_data_hooks = m_context.get_client_data_hooks ())
3523 228 : if (const client_version_info *vinfo
3524 114 : = client_data_hooks->get_any_version_info ())
3525 : {
3526 114 : class my_plugin_visitor : public client_version_info :: plugin_visitor
3527 : {
3528 : public:
3529 : void
3530 11 : on_plugin (const client_plugin_info &p) final override
3531 : {
3532 : /* Create a "toolComponent" object (SARIF v2.1.0 section 3.19)
3533 : for the plugin. */
3534 11 : auto plugin_obj = std::make_unique<sarif_tool_component> ();
3535 :
3536 : /* "name" property (SARIF v2.1.0 section 3.19.8). */
3537 11 : if (const char *short_name = p.get_short_name ())
3538 11 : plugin_obj->set_string ("name", short_name);
3539 :
3540 : /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
3541 11 : if (const char *full_name = p.get_full_name ())
3542 11 : plugin_obj->set_string ("fullName", full_name);
3543 :
3544 : /* "version" property (SARIF v2.1.0 section 3.19.13). */
3545 11 : if (const char *version = p.get_version ())
3546 0 : plugin_obj->set_string ("version", version);
3547 :
3548 11 : m_plugin_objs.push_back (std::move (plugin_obj));
3549 11 : }
3550 : std::vector<std::unique_ptr<sarif_tool_component>> m_plugin_objs;
3551 : };
3552 114 : my_plugin_visitor v;
3553 114 : vinfo->for_each_plugin (v);
3554 114 : if (v.m_plugin_objs.size () > 0)
3555 : {
3556 11 : auto extensions_arr = std::make_unique<json::array> ();
3557 22 : for (auto &iter : v.m_plugin_objs)
3558 11 : extensions_arr->append<sarif_tool_component> (std::move (iter));
3559 11 : tool_obj->set<json::array> ("extensions",
3560 : std::move (extensions_arr));
3561 11 : }
3562 114 : }
3563 :
3564 : /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other
3565 : "extensions" (see toplev.cc: print_version). */
3566 :
3567 282 : return tool_obj;
3568 : }
3569 :
3570 : /* Make a "toolComponent" object (SARIF v2.1.0 section 3.19) for what SARIF
3571 : calls the "driver" (see SARIF v2.1.0 section 3.18.1). */
3572 :
3573 : std::unique_ptr<sarif_tool_component>
3574 282 : sarif_builder::make_driver_tool_component_object ()
3575 : {
3576 282 : auto driver_obj = std::make_unique<sarif_tool_component> ();
3577 :
3578 282 : if (auto client_data_hooks = m_context.get_client_data_hooks ())
3579 228 : if (const client_version_info *vinfo
3580 114 : = client_data_hooks->get_any_version_info ())
3581 : {
3582 : /* "name" property (SARIF v2.1.0 section 3.19.8). */
3583 114 : if (const char *name = vinfo->get_tool_name ())
3584 114 : driver_obj->set_string ("name", name);
3585 :
3586 : /* "fullName" property (SARIF v2.1.0 section 3.19.9). */
3587 114 : if (char *full_name = vinfo->maybe_make_full_name ())
3588 : {
3589 114 : driver_obj->set_string ("fullName", full_name);
3590 114 : free (full_name);
3591 : }
3592 :
3593 : /* "version" property (SARIF v2.1.0 section 3.19.13). */
3594 114 : if (const char *version = vinfo->get_version_string ())
3595 114 : driver_obj->set_string ("version", version);
3596 :
3597 : /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */
3598 114 : if (char *version_url = vinfo->maybe_make_version_url ())
3599 : {
3600 114 : driver_obj->set_string ("informationUri", version_url);
3601 114 : free (version_url);
3602 : }
3603 : }
3604 :
3605 : /* "rules" property (SARIF v2.1.0 section 3.19.23). */
3606 282 : driver_obj->set<json::array> ("rules", std::move (m_rules_arr));
3607 :
3608 282 : return driver_obj;
3609 : }
3610 :
3611 : /* If we've seen any CWE IDs, make an array for the "taxonomies" property
3612 : (SARIF v2.1.0 section 3.14.8) of a run object, containing a single
3613 : "toolComponent" (3.19) as per 3.19.3, representing the CWE.
3614 :
3615 : Otherwise return nullptr. */
3616 :
3617 : std::unique_ptr<json::array>
3618 282 : sarif_builder::maybe_make_taxonomies_array () const
3619 : {
3620 282 : auto cwe_obj = maybe_make_cwe_taxonomy_object ();
3621 282 : if (!cwe_obj)
3622 260 : return nullptr;
3623 :
3624 : /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */
3625 22 : auto taxonomies_arr = std::make_unique<json::array> ();
3626 22 : taxonomies_arr->append<sarif_tool_component> (std::move (cwe_obj));
3627 22 : return taxonomies_arr;
3628 282 : }
3629 :
3630 : /* If we've seen any CWE IDs, make a "toolComponent" object
3631 : (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3.
3632 : Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set.
3633 :
3634 : Otherwise return nullptr. */
3635 :
3636 : std::unique_ptr<sarif_tool_component>
3637 282 : sarif_builder::maybe_make_cwe_taxonomy_object () const
3638 : {
3639 282 : if (m_cwe_id_set.is_empty ())
3640 260 : return nullptr;
3641 :
3642 22 : auto taxonomy_obj = std::make_unique<sarif_tool_component> ();
3643 :
3644 : /* "name" property (SARIF v2.1.0 section 3.19.8). */
3645 22 : taxonomy_obj->set_string ("name", "CWE");
3646 :
3647 : /* "version" property (SARIF v2.1.0 section 3.19.13). */
3648 22 : taxonomy_obj->set_string ("version", "4.7");
3649 :
3650 : /* "organization" property (SARIF v2.1.0 section 3.19.18). */
3651 22 : taxonomy_obj->set_string ("organization", "MITRE");
3652 :
3653 : /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */
3654 22 : taxonomy_obj->set<sarif_multiformat_message_string>
3655 22 : ("shortDescription",
3656 22 : make_multiformat_message_string ("The MITRE"
3657 : " Common Weakness Enumeration"));
3658 :
3659 : /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */
3660 22 : auto taxa_arr = std::make_unique<json::array> ();
3661 44 : for (auto cwe_id : m_cwe_id_set)
3662 22 : taxa_arr->append<sarif_reporting_descriptor>
3663 22 : (make_reporting_descriptor_object_for_cwe_id (cwe_id));
3664 22 : taxonomy_obj->set<json::array> ("taxa", std::move (taxa_arr));
3665 :
3666 22 : return taxonomy_obj;
3667 22 : }
3668 :
3669 : /* Ensure that we have an "artifact" object (SARIF v2.1.0 section 3.24)
3670 : for FILENAME, adding it to m_filename_to_artifact_map if not already
3671 : found, and adding ROLE to it.
3672 : If EMBED_CONTENTS is true, then flag that we will attempt to embed the
3673 : contents of this artifact when writing it out. */
3674 :
3675 : sarif_artifact &
3676 1141 : sarif_builder::get_or_create_artifact (const char *filename,
3677 : enum diagnostic_artifact_role role,
3678 : bool embed_contents)
3679 : {
3680 1141 : if (auto *slot = m_filename_to_artifact_map.get (filename))
3681 : {
3682 705 : (*slot)->add_role (role, embed_contents);
3683 705 : return **slot;
3684 : }
3685 :
3686 436 : sarif_artifact *artifact_obj = new sarif_artifact (filename);
3687 436 : artifact_obj->add_role (role, embed_contents);
3688 436 : m_filename_to_artifact_map.put (filename, artifact_obj);
3689 :
3690 : /* "location" property (SARIF v2.1.0 section 3.24.2). */
3691 436 : artifact_obj->set<sarif_artifact_location>
3692 436 : ("location", make_artifact_location_object (filename));
3693 :
3694 : /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */
3695 436 : switch (role)
3696 : {
3697 0 : default:
3698 0 : gcc_unreachable ();
3699 436 : case diagnostic_artifact_role::analysis_target:
3700 436 : case diagnostic_artifact_role::result_file:
3701 436 : case diagnostic_artifact_role::scanned_file:
3702 436 : case diagnostic_artifact_role::traced_file:
3703 : /* Assume that these are in the source language. */
3704 436 : if (auto client_data_hooks = m_context.get_client_data_hooks ())
3705 264 : if (const char *source_lang
3706 132 : = client_data_hooks->maybe_get_sarif_source_language (filename))
3707 132 : artifact_obj->set_string ("sourceLanguage", source_lang);
3708 : break;
3709 :
3710 : case diagnostic_artifact_role::debug_output_file:
3711 : /* Assume that these are not in the source language. */
3712 : break;
3713 : }
3714 :
3715 : return *artifact_obj;
3716 : }
3717 :
3718 : /* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for the
3719 : full contents of FILENAME. */
3720 :
3721 : std::unique_ptr<sarif_artifact_content>
3722 241 : sarif_builder::maybe_make_artifact_content_object (const char *filename) const
3723 : {
3724 : /* Let input.cc handle any charset conversion. */
3725 241 : char_span utf8_content
3726 241 : = m_context.get_file_cache ().get_source_file_content (filename);
3727 241 : if (!utf8_content)
3728 4 : return nullptr;
3729 :
3730 : /* Don't add it if it's not valid UTF-8. */
3731 237 : if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ()))
3732 12 : return nullptr;
3733 :
3734 225 : auto artifact_content_obj = std::make_unique<sarif_artifact_content> ();
3735 225 : artifact_content_obj->set<json::string>
3736 450 : ("text",
3737 225 : std::make_unique <json::string> (utf8_content.get_buffer (),
3738 225 : utf8_content.length ()));
3739 225 : return artifact_content_obj;
3740 225 : }
3741 :
3742 : /* Attempt to read the given range of lines from FILENAME; return
3743 : a freshly-allocated 0-terminated buffer containing them, or nullptr. */
3744 :
3745 : char *
3746 847 : sarif_builder::get_source_lines (const char *filename,
3747 : int start_line,
3748 : int end_line) const
3749 : {
3750 847 : auto_vec<char> result;
3751 :
3752 1694 : for (int line = start_line; line <= end_line; line++)
3753 : {
3754 847 : char_span line_content
3755 847 : = m_context.get_file_cache ().get_source_line (filename, line);
3756 847 : if (!line_content.get_buffer ())
3757 0 : return nullptr;
3758 847 : result.reserve (line_content.length () + 1);
3759 24029 : for (size_t i = 0; i < line_content.length (); i++)
3760 23182 : result.quick_push (line_content[i]);
3761 847 : result.quick_push ('\n');
3762 : }
3763 847 : result.safe_push ('\0');
3764 :
3765 1694 : return xstrdup (result.address ());
3766 847 : }
3767 :
3768 : /* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for the given
3769 : run of lines within FILENAME (including the endpoints).
3770 : If R is non-NULL, use it to potentially set the "rendered"
3771 : property (3.3.4). */
3772 :
3773 : std::unique_ptr<sarif_artifact_content>
3774 847 : sarif_builder::
3775 : maybe_make_artifact_content_object (const char *filename,
3776 : int start_line,
3777 : int end_line,
3778 : const content_renderer *r) const
3779 : {
3780 847 : char *text_utf8 = get_source_lines (filename, start_line, end_line);
3781 :
3782 847 : if (!text_utf8)
3783 0 : return nullptr;
3784 :
3785 : /* Don't add it if it's not valid UTF-8. */
3786 847 : if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8)))
3787 : {
3788 172 : free (text_utf8);
3789 172 : return nullptr;
3790 : }
3791 :
3792 675 : auto artifact_content_obj = std::make_unique<sarif_artifact_content> ();
3793 675 : artifact_content_obj->set_string ("text", text_utf8);
3794 675 : free (text_utf8);
3795 :
3796 : /* 3.3.4 "rendered" property. */
3797 675 : if (r)
3798 136 : if (std::unique_ptr<sarif_multiformat_message_string> rendered
3799 136 : = r->render (*this))
3800 136 : artifact_content_obj->set ("rendered", std::move (rendered));
3801 :
3802 675 : return artifact_content_obj;
3803 675 : }
3804 :
3805 : /* Attempt to generate a "message" object describing a fix-it hint,
3806 : or null if there was a problem. */
3807 :
3808 : std::unique_ptr<sarif_message>
3809 9 : sarif_builder::
3810 : make_message_describing_fix_it_hint (const fixit_hint &hint) const
3811 : {
3812 9 : pretty_printer pp;
3813 9 : if (hint.insertion_p ())
3814 4 : pp_printf (&pp, G_("Insert %qs"), hint.get_string ());
3815 : else
3816 : {
3817 : /* Try to get prior content. */
3818 5 : expanded_location start = expand_location (hint.get_start_loc ());
3819 5 : expanded_location next_loc = expand_location (hint.get_next_loc ());
3820 5 : if (start.file != next_loc.file)
3821 0 : return nullptr;
3822 5 : if (start.line != next_loc.line)
3823 0 : return nullptr;
3824 5 : if (start.column == 0)
3825 0 : return nullptr;
3826 5 : if (next_loc.column == 0)
3827 0 : return nullptr;
3828 :
3829 5 : const int start_offset = start.column - 1;
3830 5 : const int next_offset = next_loc.column - 1;
3831 5 : if (next_offset <= start_offset)
3832 0 : return nullptr;
3833 :
3834 5 : size_t victim_len = next_offset - start_offset;
3835 :
3836 5 : char_span existing_line = get_context ()
3837 5 : .get_file_cache ()
3838 5 : .get_source_line (start.file, start.line);
3839 5 : if (!existing_line)
3840 0 : return nullptr;
3841 :
3842 5 : label_text existing_text
3843 5 : = label_text::take (existing_line.subspan (start_offset,
3844 5 : victim_len).xstrdup ());
3845 :
3846 5 : if (hint.deletion_p ())
3847 : {
3848 : // Removal
3849 1 : pp_printf (&pp, G_("Delete %qs"),
3850 : existing_text.get ());
3851 : }
3852 : else
3853 : {
3854 : // Replacement
3855 4 : gcc_assert (hint.replacement_p ());
3856 4 : pp_printf (&pp, G_("Replace %qs with %qs"),
3857 : existing_text.get (),
3858 : hint.get_string ());
3859 : }
3860 5 : }
3861 9 : return make_message_object (pp_formatted_text (&pp));
3862 9 : }
3863 :
3864 : /* Make a "fix" object (SARIF v2.1.0 section 3.55) for RICHLOC. */
3865 :
3866 : std::unique_ptr<sarif_fix>
3867 9 : sarif_builder::make_fix_object (const rich_location &richloc)
3868 : {
3869 9 : auto fix_obj = std::make_unique<sarif_fix> ();
3870 :
3871 : /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */
3872 : /* We assume that all fix-it hints in RICHLOC affect the same file. */
3873 9 : auto artifact_change_arr = std::make_unique<json::array> ();
3874 9 : artifact_change_arr->append<sarif_artifact_change>
3875 9 : (make_artifact_change_object (richloc));
3876 9 : fix_obj->set<json::array> ("artifactChanges",
3877 : std::move (artifact_change_arr));
3878 :
3879 : // 3.55.2 "description" property
3880 : /* Attempt to generate a description. We can only do this
3881 : if there was a single hint. */
3882 9 : if (richloc.get_num_fixit_hints () == 1)
3883 : {
3884 9 : const fixit_hint *hint = richloc.get_fixit_hint (0);
3885 9 : gcc_assert (hint);
3886 9 : if (auto desc_msg = make_message_describing_fix_it_hint (*hint))
3887 9 : fix_obj->set<sarif_message> ("description",
3888 9 : std::move (desc_msg));
3889 : }
3890 :
3891 18 : return fix_obj;
3892 9 : }
3893 :
3894 : /* Make an "artifactChange" object (SARIF v2.1.0 section 3.56) for RICHLOC. */
3895 :
3896 : std::unique_ptr<sarif_artifact_change>
3897 9 : sarif_builder::make_artifact_change_object (const rich_location &richloc)
3898 : {
3899 9 : auto artifact_change_obj = std::make_unique<sarif_artifact_change> ();
3900 :
3901 : /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */
3902 9 : artifact_change_obj->set<sarif_artifact_location>
3903 9 : ("artifactLocation",
3904 9 : make_artifact_location_object (richloc.get_loc ()));
3905 :
3906 : /* "replacements" property (SARIF v2.1.0 section 3.56.3). */
3907 9 : auto replacement_arr = std::make_unique<json::array> ();
3908 18 : for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++)
3909 : {
3910 9 : const fixit_hint *hint = richloc.get_fixit_hint (i);
3911 9 : replacement_arr->append<sarif_replacement>
3912 9 : (make_replacement_object (*hint));
3913 : }
3914 9 : artifact_change_obj->set<json::array> ("replacements",
3915 : std::move (replacement_arr));
3916 :
3917 18 : return artifact_change_obj;
3918 9 : }
3919 :
3920 : /* Make a "replacement" object (SARIF v2.1.0 section 3.57) for HINT. */
3921 :
3922 : std::unique_ptr<sarif_replacement>
3923 9 : sarif_builder::make_replacement_object (const fixit_hint &hint) const
3924 : {
3925 9 : auto replacement_obj = std::make_unique<sarif_replacement> ();
3926 :
3927 : /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */
3928 9 : replacement_obj->set<sarif_region> ("deletedRegion",
3929 9 : make_region_object_for_hint (hint));
3930 :
3931 : /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */
3932 9 : replacement_obj->set<sarif_artifact_content>
3933 9 : ("insertedContent",
3934 9 : make_artifact_content_object (hint.get_string ()));
3935 :
3936 9 : return replacement_obj;
3937 : }
3938 :
3939 : /* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for TEXT. */
3940 :
3941 : std::unique_ptr<sarif_artifact_content>
3942 9 : sarif_builder::make_artifact_content_object (const char *text) const
3943 : {
3944 9 : auto content_obj = std::make_unique<sarif_artifact_content> ();
3945 :
3946 : /* "text" property (SARIF v2.1.0 section 3.3.2). */
3947 9 : content_obj->set_string ("text", text);
3948 :
3949 9 : return content_obj;
3950 : }
3951 :
3952 : /* class sarif_sink_buffer : public per_sink_buffer. */
3953 :
3954 : void
3955 0 : sarif_sink_buffer::dump (FILE *out, int indent) const
3956 : {
3957 0 : dumping::emit_heading (out, indent, "sarif_sink_buffer");
3958 0 : int idx = 0;
3959 0 : for (auto &result : m_results)
3960 : {
3961 0 : dumping::emit_indent (out, indent + 2);
3962 0 : fprintf (out, "result[%i]:\n", idx);
3963 0 : result->dump (out, true);
3964 0 : fprintf (out, "\n");
3965 0 : ++idx;
3966 : }
3967 0 : }
3968 :
3969 : bool
3970 61 : sarif_sink_buffer::empty_p () const
3971 : {
3972 61 : return m_results.empty ();
3973 : }
3974 :
3975 : void
3976 0 : sarif_sink_buffer::move_to (per_sink_buffer &base)
3977 : {
3978 0 : sarif_sink_buffer &dest
3979 : = static_cast<sarif_sink_buffer &> (base);
3980 0 : for (auto &&result : m_results)
3981 0 : dest.m_results.push_back (std::move (result));
3982 0 : m_results.clear ();
3983 0 : }
3984 :
3985 : void
3986 22 : sarif_sink_buffer::clear ()
3987 : {
3988 22 : m_results.clear ();
3989 22 : }
3990 :
3991 : void
3992 8 : sarif_sink_buffer::flush ()
3993 : {
3994 16 : for (auto &&result : m_results)
3995 : {
3996 8 : result->process_worklist (m_builder);
3997 8 : m_builder.m_results_array->append<sarif_result> (std::move (result));
3998 : }
3999 8 : m_results.clear ();
4000 8 : }
4001 :
4002 : class sarif_sink : public sink
4003 : {
4004 : public:
4005 290 : ~sarif_sink ()
4006 290 : {
4007 : /* Any sarifResult objects should have been handled by now.
4008 : If not, then something's gone wrong with diagnostic
4009 : groupings. */
4010 290 : std::unique_ptr<sarif_result> pending_result
4011 290 : = m_builder.take_current_result ();
4012 290 : gcc_assert (!pending_result);
4013 290 : }
4014 :
4015 0 : void dump (FILE *out, int indent) const override
4016 : {
4017 0 : sink::dump (out, indent);
4018 0 : dumping::emit_heading (out, indent, "sarif_builder");
4019 0 : m_builder.dump (out, indent + 2);
4020 0 : }
4021 :
4022 : void
4023 290 : set_main_input_filename (const char *name) final override
4024 : {
4025 114 : m_builder.set_main_input_filename (name);
4026 114 : }
4027 :
4028 : std::unique_ptr<per_sink_buffer>
4029 17 : make_per_sink_buffer () final override
4030 : {
4031 17 : return std::make_unique<sarif_sink_buffer> (m_builder);
4032 : }
4033 34 : void set_buffer (per_sink_buffer *base_buffer) final override
4034 : {
4035 34 : sarif_sink_buffer *buffer
4036 : = static_cast<sarif_sink_buffer *> (base_buffer);
4037 34 : m_buffer = buffer;
4038 34 : }
4039 :
4040 0 : bool follows_reference_printer_p () const final override
4041 : {
4042 0 : return false;
4043 : }
4044 :
4045 290 : void update_printer () final override
4046 : {
4047 290 : m_printer = m_context.clone_printer ();
4048 :
4049 : /* Don't colorize the text. */
4050 290 : pp_show_color (m_printer.get ()) = false;
4051 :
4052 : /* No textual URLs. */
4053 290 : m_printer->set_url_format (URL_FORMAT_NONE);
4054 :
4055 : /* Use builder's token printer. */
4056 290 : get_printer ()->set_token_printer (&m_builder.get_token_printer ());
4057 :
4058 : /* Update the builder to use the new printer. */
4059 290 : m_builder.set_printer (*get_printer ());
4060 290 : }
4061 :
4062 483 : void on_begin_group () final override
4063 : {
4064 : /* No-op, */
4065 483 : }
4066 483 : void on_end_group () override
4067 : {
4068 483 : m_builder.end_group ();
4069 483 : }
4070 : void
4071 602 : on_report_diagnostic (const diagnostic_info &diagnostic,
4072 : enum kind orig_diag_kind) final override
4073 : {
4074 602 : DIAGNOSTICS_LOG_SCOPE_PRINTF0
4075 : (get_logger (),
4076 602 : "diagnostics::sarif_sink::on_report_diagnostic");
4077 602 : m_builder.on_report_diagnostic (diagnostic, orig_diag_kind, m_buffer);
4078 602 : }
4079 4 : void on_diagram (const diagram &d) final override
4080 : {
4081 4 : m_builder.emit_diagram (d);
4082 4 : }
4083 574 : void after_diagnostic (const diagnostic_info &) final override
4084 : {
4085 : /* No-op. */
4086 574 : }
4087 :
4088 : void
4089 1 : report_global_digraph (const lazily_created<digraphs::digraph> &ldg)
4090 : final override
4091 : {
4092 1 : m_builder.report_global_digraph (ldg);
4093 1 : }
4094 :
4095 : void
4096 45 : report_digraph_for_logical_location (const lazily_created<digraphs::digraph> &ldg,
4097 : logical_locations::key key) final override
4098 : {
4099 45 : m_builder.report_digraph_for_logical_location (ldg, key);
4100 45 : }
4101 :
4102 : sarif_builder &get_builder () { return m_builder; }
4103 :
4104 96 : size_t num_results () const { return m_builder.num_results (); }
4105 16 : sarif_result &get_result (size_t idx) { return m_builder.get_result (idx); }
4106 :
4107 : protected:
4108 290 : sarif_sink (context &dc,
4109 : const line_maps *line_maps,
4110 : std::unique_ptr<sarif_serialization_format> serialization_format,
4111 : const sarif_generation_options &sarif_gen_opts)
4112 290 : : sink (dc),
4113 290 : m_builder (dc, *get_printer (), line_maps,
4114 : std::move (serialization_format), sarif_gen_opts),
4115 290 : m_buffer (nullptr)
4116 290 : {}
4117 :
4118 : sarif_builder m_builder;
4119 : sarif_sink_buffer *m_buffer;
4120 : };
4121 :
4122 : class sarif_stream_sink : public sarif_sink
4123 : {
4124 : public:
4125 0 : sarif_stream_sink (context &dc,
4126 : const line_maps *line_maps,
4127 : std::unique_ptr<sarif_serialization_format> serialization_format,
4128 : const sarif_generation_options &sarif_gen_opts,
4129 : FILE *stream)
4130 0 : : sarif_sink (dc, line_maps,
4131 : std::move (serialization_format), sarif_gen_opts),
4132 0 : m_stream (stream)
4133 : {
4134 0 : }
4135 0 : ~sarif_stream_sink ()
4136 0 : {
4137 0 : m_builder.flush_to_file (m_stream);
4138 0 : }
4139 0 : void dump_kind (FILE *out) const override
4140 : {
4141 0 : fprintf (out, "sarif_stream_sink");
4142 0 : }
4143 0 : bool machine_readable_stderr_p () const final override
4144 : {
4145 0 : return m_stream == stderr;
4146 : }
4147 : private:
4148 : FILE *m_stream;
4149 : };
4150 :
4151 : class sarif_file_sink : public sarif_sink
4152 : {
4153 : public:
4154 114 : sarif_file_sink (context &dc,
4155 : const line_maps *line_maps,
4156 : std::unique_ptr<sarif_serialization_format> serialization_format,
4157 : const sarif_generation_options &sarif_gen_opts,
4158 : output_file output_file_)
4159 114 : : sarif_sink (dc, line_maps,
4160 : std::move (serialization_format),
4161 : sarif_gen_opts),
4162 114 : m_output_file (std::move (output_file_))
4163 : {
4164 114 : gcc_assert (m_output_file.get_open_file ());
4165 114 : gcc_assert (m_output_file.get_filename ());
4166 114 : }
4167 228 : ~sarif_file_sink ()
4168 114 : {
4169 114 : m_builder.flush_to_file (m_output_file.get_open_file ());
4170 228 : }
4171 0 : void dump_kind (FILE *out) const override
4172 : {
4173 0 : fprintf (out, "sarif_file_sink: %s",
4174 : m_output_file.get_filename ());
4175 0 : }
4176 15 : bool machine_readable_stderr_p () const final override
4177 : {
4178 15 : return false;
4179 : }
4180 :
4181 : private:
4182 : output_file m_output_file;
4183 : };
4184 :
4185 : class unique_fd
4186 : {
4187 : public:
4188 : unique_fd () : m_ival (-1) {}
4189 0 : explicit unique_fd (int ival) : m_ival (ival) {}
4190 : unique_fd (const unique_fd &) = delete;
4191 0 : unique_fd (unique_fd &&other)
4192 0 : : m_ival (other.m_ival)
4193 : {
4194 0 : other.m_ival = -1;
4195 : }
4196 0 : ~unique_fd ()
4197 : {
4198 0 : if (m_ival != -1)
4199 0 : close (m_ival);
4200 0 : }
4201 : unique_fd &operator= (const unique_fd &other) = delete;
4202 : unique_fd &operator= (unique_fd &&other)
4203 : {
4204 : if (m_ival != -1)
4205 : close (m_ival);
4206 : m_ival = other.m_ival;
4207 : other.m_ival = -1;
4208 : return *this;
4209 : }
4210 :
4211 0 : operator int () const { return m_ival; }
4212 :
4213 : private:
4214 : int m_ival;
4215 : };
4216 :
4217 : class sarif_socket_sink : public sarif_sink
4218 : {
4219 : public:
4220 0 : sarif_socket_sink (context &dc,
4221 : const line_maps *line_maps,
4222 : std::unique_ptr<sarif_serialization_format> serialization_format,
4223 : const sarif_generation_options &sarif_gen_opts,
4224 : unique_fd fd)
4225 0 : : sarif_sink (dc, line_maps,
4226 : std::move (serialization_format),
4227 : sarif_gen_opts),
4228 0 : m_fd (std::move (fd))
4229 : {
4230 0 : }
4231 0 : void dump_kind (FILE *out) const override
4232 : {
4233 0 : fprintf (out, "sarif_socket_sink: fd=%i", int (m_fd));
4234 0 : }
4235 0 : bool machine_readable_stderr_p () const final override
4236 : {
4237 0 : return false;
4238 : }
4239 :
4240 : /* Rather than appending it to the results array, instead
4241 : send it to the output socket as a JSON-RPC 2.0 notification. */
4242 0 : void on_end_group () final override
4243 : {
4244 : // TODO: what about buffering?
4245 :
4246 0 : std::unique_ptr<sarif_result> result = m_builder.take_current_result ();
4247 0 : if (!result)
4248 0 : return;
4249 :
4250 0 : auto notification = std::make_unique<json::object> ();
4251 0 : notification->set_string ("jsonrpc", "2.0");
4252 0 : notification->set_string ("method", "OnSarifResult");
4253 0 : {
4254 0 : auto params = std::make_unique<json::object> ();
4255 0 : params->set ("result", std::move (result));
4256 0 : notification->set ("params", std::move (params));
4257 0 : }
4258 :
4259 0 : send_rpc_notification (*notification);
4260 0 : }
4261 :
4262 : private:
4263 : void
4264 0 : send_rpc_notification (const json::object ¬ification)
4265 : {
4266 0 : DIAGNOSTICS_LOG_SCOPE_PRINTF0 (m_context.get_logger (),
4267 0 : "sarif_socket_sink::send_rpc_notification");
4268 0 : const bool formatted = false;
4269 0 : pretty_printer pp_content;
4270 0 : notification.print (&pp_content, formatted);
4271 0 : size_t content_length = strlen (pp_formatted_text (&pp_content));
4272 :
4273 0 : pretty_printer pp_header;
4274 : #if __GNUC__ >= 10
4275 0 : # pragma GCC diagnostic push
4276 0 : # pragma GCC diagnostic ignored "-Wformat-diag"
4277 : #endif
4278 0 : pp_printf (&pp_header, "Content-Length: %li\n\n", content_length);
4279 : #if __GNUC__ >= 10
4280 0 : # pragma GCC diagnostic pop
4281 : #endif
4282 0 : size_t header_length = strlen (pp_formatted_text (&pp_header));
4283 :
4284 0 : size_t output_size = header_length + content_length;
4285 0 : char *buf = (char *)xmalloc (output_size);
4286 0 : memcpy (buf, pp_formatted_text (&pp_header), header_length);
4287 0 : memcpy (buf + header_length,
4288 0 : pp_formatted_text (&pp_content),
4289 : content_length);
4290 :
4291 : /* TODO: should we attempt to handle partial writes here? */
4292 0 : write (m_fd, buf, output_size);
4293 :
4294 0 : free (buf);
4295 0 : }
4296 :
4297 : unique_fd m_fd;
4298 : };
4299 :
4300 : /* Print the start of an embedded link to PP, as per 3.11.6. */
4301 :
4302 : static void
4303 43 : sarif_begin_embedded_link (pretty_printer *pp)
4304 : {
4305 0 : pp_character (pp, '[');
4306 17 : }
4307 :
4308 : /* Print the end of an embedded link to PP, as per 3.11.6. */
4309 :
4310 : static void
4311 43 : sarif_end_embedded_link (pretty_printer *pp,
4312 : const char *url)
4313 : {
4314 43 : pp_string (pp, "](");
4315 : /* TODO: does the URI need escaping?
4316 : See https://github.com/oasis-tcs/sarif-spec/issues/657 */
4317 43 : pp_string (pp, url);
4318 43 : pp_character (pp, ')');
4319 43 : }
4320 :
4321 : /* class sarif_token_printer : public token_printer. */
4322 :
4323 : /* Implementation of pretty_printer::token_printer for SARIF output.
4324 : Emit URLs as per 3.11.6 ("Messages with embedded links"). */
4325 :
4326 : void
4327 792 : sarif_builder::sarif_token_printer::print_tokens (pretty_printer *pp,
4328 : const pp_token_list &tokens)
4329 : {
4330 : /* Convert to text, possibly with colorization, URLs, etc. */
4331 792 : label_text current_url;
4332 4218 : for (auto iter = tokens.m_first; iter; iter = iter->m_next)
4333 3426 : switch (iter->m_kind)
4334 : {
4335 0 : default:
4336 0 : gcc_unreachable ();
4337 :
4338 1896 : case pp_token::kind::text:
4339 1896 : {
4340 1896 : const pp_token_text *sub = as_a <const pp_token_text *> (iter);
4341 1896 : const char * const str = sub->m_value.get ();
4342 1896 : if (current_url.get ())
4343 : {
4344 : /* Write iter->m_value, but escaping any
4345 : escaped link characters as per 3.11.6. */
4346 296 : for (const char *ptr = str; *ptr; ptr++)
4347 : {
4348 270 : const char ch = *ptr;
4349 270 : switch (ch)
4350 : {
4351 230 : default:
4352 230 : pp_character (pp, ch);
4353 230 : break;
4354 40 : case '\\':
4355 40 : case '[':
4356 40 : case ']':
4357 40 : pp_character (pp, '\\');
4358 40 : pp_character (pp, ch);
4359 40 : break;
4360 : }
4361 : }
4362 : }
4363 : else
4364 : /* TODO: is other escaping needed? (e.g. of '[')
4365 : See https://github.com/oasis-tcs/sarif-spec/issues/658 */
4366 1870 : pp_string (pp, str);
4367 : }
4368 : break;
4369 :
4370 : case pp_token::kind::begin_color:
4371 : case pp_token::kind::end_color:
4372 : /* These are no-ops. */
4373 : break;
4374 :
4375 701 : case pp_token::kind::begin_quote:
4376 701 : pp_begin_quote (pp, pp_show_color (pp));
4377 701 : break;
4378 659 : case pp_token::kind::end_quote:
4379 659 : pp_end_quote (pp, pp_show_color (pp));
4380 659 : break;
4381 :
4382 : /* Emit URLs as per 3.11.6 ("Messages with embedded links"). */
4383 26 : case pp_token::kind::begin_url:
4384 26 : {
4385 26 : pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter);
4386 26 : sarif_begin_embedded_link (pp);
4387 26 : current_url = std::move (sub->m_value);
4388 : }
4389 26 : break;
4390 26 : case pp_token::kind::end_url:
4391 26 : gcc_assert (current_url.get ());
4392 26 : sarif_end_embedded_link (pp, current_url.get ());
4393 26 : current_url = label_text::borrow (nullptr);
4394 26 : break;
4395 :
4396 114 : case pp_token::kind::event_id:
4397 114 : {
4398 114 : pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);
4399 114 : gcc_assert (sub->m_event_id.known_p ());
4400 114 : const sarif_code_flow *code_flow
4401 114 : = m_builder.get_code_flow_for_event_ids ();
4402 114 : label_text url = make_sarif_url_for_event (code_flow,
4403 114 : sub->m_event_id);
4404 114 : if (url.get ())
4405 17 : sarif_begin_embedded_link (pp);
4406 114 : pp_character (pp, '(');
4407 114 : pp_decimal_int (pp, sub->m_event_id.one_based ());
4408 114 : pp_character (pp, ')');
4409 114 : if (url.get ())
4410 17 : sarif_end_embedded_link (pp, url.get ());
4411 114 : }
4412 114 : break;
4413 : }
4414 792 : }
4415 :
4416 : /* Populate CONTEXT in preparation for SARIF output (either to stderr, or
4417 : to a file).
4418 : Return a reference to *FMT. */
4419 :
4420 : static sink &
4421 266 : init_sarif_sink (context &dc,
4422 : std::unique_ptr<sarif_sink> fmt)
4423 : {
4424 266 : gcc_assert (fmt);
4425 266 : sink &out = *fmt;
4426 :
4427 266 : fmt->update_printer ();
4428 :
4429 266 : dc.set_sink (std::move (fmt));
4430 :
4431 266 : return out;
4432 : }
4433 :
4434 : /* Populate DC in preparation for SARIF output to stderr.
4435 : Return a reference to the new sink. */
4436 :
4437 : sink &
4438 0 : init_sarif_stderr (context &dc,
4439 : const line_maps *line_maps,
4440 : bool formatted)
4441 : {
4442 0 : gcc_assert (line_maps);
4443 0 : const sarif_generation_options sarif_gen_opts;
4444 0 : auto serialization
4445 0 : = std::make_unique<sarif_serialization_format_json> (formatted);
4446 0 : return init_sarif_sink
4447 0 : (dc,
4448 0 : std::make_unique<sarif_stream_sink> (dc,
4449 : line_maps,
4450 : std::move (serialization),
4451 : sarif_gen_opts,
4452 0 : stderr));
4453 0 : }
4454 :
4455 : /* Attempt to open "BASE_FILE_NAME""EXTENSION" for writing.
4456 : Return a non-null output_file,
4457 : or return a null output_file and complain to DC
4458 : using LINE_MAPS. */
4459 :
4460 : output_file
4461 112 : output_file::try_to_open (context &dc,
4462 : line_maps *line_maps,
4463 : const char *base_file_name,
4464 : const char *extension,
4465 : bool is_binary)
4466 : {
4467 112 : gcc_assert (extension);
4468 112 : gcc_assert (extension[0] == '.');
4469 :
4470 112 : if (!base_file_name)
4471 : {
4472 0 : rich_location richloc (line_maps, UNKNOWN_LOCATION);
4473 0 : dc.emit_diagnostic_with_group
4474 0 : (kind::error, richloc, nullptr, 0,
4475 : "unable to determine filename for SARIF output");
4476 0 : return output_file ();
4477 0 : }
4478 :
4479 112 : label_text filename = label_text::take (concat (base_file_name,
4480 : extension,
4481 112 : nullptr));
4482 224 : FILE *outf = fopen (filename.get (), is_binary ? "wb" : "w");
4483 112 : if (!outf)
4484 : {
4485 0 : rich_location richloc (line_maps, UNKNOWN_LOCATION);
4486 0 : dc.emit_diagnostic_with_group
4487 0 : (kind::error, richloc, nullptr, 0,
4488 : "unable to open %qs for diagnostic output: %m",
4489 : filename.get ());
4490 0 : return output_file ();
4491 0 : }
4492 112 : return output_file (outf, true, std::move (filename));
4493 112 : }
4494 :
4495 : /* Attempt to open BASE_FILE_NAME.sarif for writing JSON.
4496 : Return a non-null output_file,
4497 : or return a null output_file and complain to DC
4498 : using LINE_MAPS. */
4499 :
4500 : output_file
4501 112 : open_sarif_output_file (context &dc,
4502 : line_maps *line_maps,
4503 : const char *base_file_name,
4504 : enum sarif_serialization_kind serialization_kind)
4505 : {
4506 112 : const char *suffix;
4507 112 : bool is_binary;
4508 112 : switch (serialization_kind)
4509 : {
4510 0 : default:
4511 0 : gcc_unreachable ();
4512 112 : case sarif_serialization_kind::json:
4513 112 : suffix = ".sarif";
4514 112 : is_binary = false;
4515 112 : break;
4516 : }
4517 :
4518 112 : return output_file::try_to_open (dc,
4519 : line_maps,
4520 : base_file_name,
4521 : suffix,
4522 112 : is_binary);
4523 : }
4524 :
4525 : /* Populate DC in preparation for SARIF output to a file named
4526 : BASE_FILE_NAME.sarif.
4527 : Return a reference to the new sink. */
4528 :
4529 : sink &
4530 90 : init_sarif_file (context &dc,
4531 : line_maps *line_maps,
4532 : bool formatted,
4533 : const char *base_file_name)
4534 : {
4535 90 : gcc_assert (line_maps);
4536 :
4537 90 : output_file output_file_
4538 : = open_sarif_output_file (dc,
4539 : line_maps,
4540 : base_file_name,
4541 90 : sarif_serialization_kind::json);
4542 90 : auto serialization
4543 90 : = std::make_unique<sarif_serialization_format_json> (formatted);
4544 :
4545 90 : const sarif_generation_options sarif_gen_opts;
4546 90 : return init_sarif_sink
4547 90 : (dc,
4548 90 : std::make_unique<sarif_file_sink> (dc,
4549 : line_maps,
4550 : std::move (serialization),
4551 : sarif_gen_opts,
4552 90 : std::move (output_file_)));
4553 90 : }
4554 :
4555 : /* Populate DC in preparation for SARIF output to STREAM.
4556 : Return a reference to the new sink. */
4557 :
4558 : sink &
4559 0 : init_sarif_stream (context &dc,
4560 : const line_maps *line_maps,
4561 : bool formatted,
4562 : FILE *stream)
4563 : {
4564 0 : gcc_assert (line_maps);
4565 0 : const sarif_generation_options sarif_gen_opts;
4566 0 : auto serialization
4567 0 : = std::make_unique<sarif_serialization_format_json> (formatted);
4568 0 : return init_sarif_sink
4569 0 : (dc,
4570 0 : std::make_unique<sarif_stream_sink> (dc,
4571 : line_maps,
4572 : std::move (serialization),
4573 : sarif_gen_opts,
4574 0 : stream));
4575 0 : }
4576 :
4577 : std::unique_ptr<sink>
4578 24 : make_sarif_sink (context &dc,
4579 : const line_maps &line_maps,
4580 : std::unique_ptr<sarif_serialization_format> serialization,
4581 : const sarif_generation_options &sarif_gen_opts,
4582 : output_file output_file_)
4583 : {
4584 24 : auto sink
4585 : = std::make_unique<sarif_file_sink> (dc,
4586 48 : &line_maps,
4587 : std::move (serialization),
4588 : sarif_gen_opts,
4589 24 : std::move (output_file_));
4590 24 : sink->update_printer ();
4591 24 : return sink;
4592 24 : }
4593 :
4594 : // struct sarif_generation_options
4595 :
4596 352 : sarif_generation_options::sarif_generation_options ()
4597 352 : : m_version (sarif_version::v2_1_0),
4598 352 : m_state_graph (false)
4599 : {
4600 352 : }
4601 :
4602 : static const char *
4603 0 : get_dump_string_for_sarif_version (enum sarif_version version)
4604 : {
4605 0 : switch (version)
4606 : {
4607 0 : default:
4608 0 : gcc_unreachable ();
4609 : case sarif_version::v2_1_0:
4610 : return "v2_1_0";
4611 0 : case sarif_version::v2_2_prerelease_2024_08_08:
4612 0 : return "v2_2_prerelease_2024_08_08";
4613 : }
4614 : }
4615 :
4616 : void
4617 0 : sarif_generation_options::dump (FILE *outfile, int indent) const
4618 : {
4619 0 : dumping::emit_string_field (outfile, indent,
4620 : "m_version",
4621 0 : get_dump_string_for_sarif_version (m_version));
4622 0 : DIAGNOSTICS_DUMPING_EMIT_BOOL_FIELD (m_state_graph);
4623 0 : }
4624 :
4625 : void
4626 288043 : maybe_open_sarif_sink_for_socket (context &dc)
4627 : {
4628 288043 : gcc_assert (line_table);
4629 :
4630 288043 : const char * const env_var_name = "EXPERIMENTAL_SARIF_SOCKET";
4631 288043 : const char * const socket_name = getenv (env_var_name);
4632 288043 : if (!socket_name)
4633 288043 : return;
4634 :
4635 0 : unique_fd sfd (socket (AF_UNIX, SOCK_STREAM, 0));
4636 0 : if (sfd == -1)
4637 0 : fatal_error (UNKNOWN_LOCATION,
4638 : "unable to create socket: %m");
4639 :
4640 0 : struct sockaddr_un addr;
4641 0 : memset (&addr, 0, sizeof (addr));
4642 0 : addr.sun_family = AF_UNIX;
4643 0 : strncpy (addr.sun_path, socket_name, sizeof (addr.sun_path) - 1);
4644 :
4645 0 : if (connect (sfd, (struct sockaddr *)&addr, sizeof (addr)) == -1)
4646 0 : fatal_error (UNKNOWN_LOCATION,
4647 : "unable to connect to %qs: %m",
4648 : socket_name);
4649 :
4650 : /* TODO: should there be a way to specify other key/value
4651 : pairs here? (as per -fdiagnostics-add-output, but as an
4652 : environment variable, perhaps). */
4653 0 : sarif_generation_options sarif_gen_opts;
4654 0 : sarif_gen_opts.m_version = sarif_version::v2_1_0;
4655 :
4656 0 : auto sink_ = std::make_unique<sarif_socket_sink>
4657 : (dc,
4658 : line_table,
4659 0 : std::make_unique <sarif_serialization_format_json> (true),
4660 : sarif_gen_opts,
4661 0 : std::move (sfd));
4662 0 : sink_->update_printer ();
4663 0 : dc.add_sink (std::move (sink_));
4664 0 : }
4665 :
4666 : #if CHECKING_P
4667 :
4668 : namespace selftest {
4669 :
4670 : using auto_fix_quotes = ::selftest::auto_fix_quotes;
4671 : using line_table_case = ::selftest::line_table_case;
4672 :
4673 : static void
4674 4 : test_sarif_array_of_unique_1 ()
4675 : {
4676 4 : sarif_array_of_unique<json::string> arr;
4677 :
4678 4 : ASSERT_EQ (arr.length (), 0);
4679 :
4680 4 : {
4681 4 : size_t idx = arr.append_uniquely (std::make_unique<json::string> ("foo"));
4682 4 : ASSERT_EQ (idx, 0);
4683 4 : ASSERT_EQ (arr.length (), 1);
4684 : }
4685 4 : {
4686 4 : size_t idx = arr.append_uniquely (std::make_unique<json::string> ("bar"));
4687 4 : ASSERT_EQ (idx, 1);
4688 4 : ASSERT_EQ (arr.length (), 2);
4689 : }
4690 :
4691 : /* Try adding them again, should be idempotent. */
4692 4 : {
4693 4 : size_t idx = arr.append_uniquely (std::make_unique<json::string> ("foo"));
4694 4 : ASSERT_EQ (idx, 0);
4695 4 : ASSERT_EQ (arr.length (), 2);
4696 : }
4697 4 : {
4698 4 : size_t idx = arr.append_uniquely (std::make_unique<json::string> ("bar"));
4699 4 : ASSERT_EQ (idx, 1);
4700 4 : ASSERT_EQ (arr.length (), 2);
4701 : }
4702 4 : }
4703 :
4704 : static void
4705 4 : test_sarif_array_of_unique_2 ()
4706 : {
4707 4 : sarif_array_of_unique<json::object> arr;
4708 :
4709 4 : ASSERT_EQ (arr.length (), 0);
4710 :
4711 4 : {
4712 4 : auto obj0 = std::make_unique<json::object> ();
4713 4 : size_t idx = arr.append_uniquely (std::move (obj0));
4714 4 : ASSERT_EQ (idx, 0);
4715 4 : ASSERT_EQ (arr.length (), 1);
4716 :
4717 : // Attempting to add another empty objects should be idempotent.
4718 4 : idx = arr.append_uniquely (std::make_unique<json::object> ());
4719 4 : ASSERT_EQ (idx, 0);
4720 4 : ASSERT_EQ (arr.length (), 1);
4721 4 : }
4722 4 : {
4723 4 : auto obj1 = std::make_unique<json::object> ();
4724 4 : obj1->set_string ("foo", "bar");
4725 4 : size_t idx = arr.append_uniquely (std::move (obj1));
4726 4 : ASSERT_EQ (idx, 1);
4727 4 : ASSERT_EQ (arr.length (), 2);
4728 :
4729 : // Attempting to add an equivalent object should be idempotent.
4730 4 : auto other = std::make_unique<json::object> ();
4731 4 : other->set_string ("foo", "bar");
4732 4 : idx = arr.append_uniquely (std::move (other));
4733 4 : ASSERT_EQ (idx, 1);
4734 4 : ASSERT_EQ (arr.length (), 2);
4735 4 : }
4736 :
4737 : // Verify behavior of add_explicit_index_values.
4738 4 : arr.add_explicit_index_values ();
4739 4 : ASSERT_JSON_INT_PROPERTY_EQ (arr[0], "index", 0);
4740 4 : ASSERT_JSON_INT_PROPERTY_EQ (arr[1], "index", 1);
4741 4 : }
4742 :
4743 : /* A subclass of sarif_sink for writing selftests.
4744 : The JSON output is cached internally, rather than written
4745 : out to a file. */
4746 :
4747 352 : class test_sarif_diagnostic_context : public test_context
4748 : {
4749 : public:
4750 176 : test_sarif_diagnostic_context (const char *main_input_filename,
4751 : const sarif_generation_options &sarif_gen_opts)
4752 176 : {
4753 176 : auto sink_ = std::make_unique<sarif_buffered_sink> (*this,
4754 : line_table,
4755 352 : true,
4756 176 : sarif_gen_opts);
4757 176 : m_sink = sink_.get (); // borrowed
4758 176 : init_sarif_sink (*this, std::move (sink_));
4759 176 : m_sink->set_main_input_filename (main_input_filename);
4760 176 : }
4761 :
4762 168 : std::unique_ptr<sarif_log> flush_to_object ()
4763 : {
4764 336 : return m_sink->flush_to_object ();
4765 : }
4766 :
4767 96 : size_t num_results () const { return m_sink->num_results (); }
4768 32 : sarif_result &get_result (size_t idx) { return m_sink->get_result (idx); }
4769 :
4770 : private:
4771 : class sarif_buffered_sink : public sarif_sink
4772 : {
4773 : public:
4774 176 : sarif_buffered_sink (context &dc,
4775 : const line_maps *line_maps,
4776 : bool formatted,
4777 : const sarif_generation_options &sarif_gen_opts)
4778 176 : : sarif_sink (dc, line_maps,
4779 176 : std::make_unique<sarif_serialization_format_json> (formatted),
4780 176 : sarif_gen_opts)
4781 : {
4782 176 : }
4783 0 : void dump_kind (FILE *out) const final override
4784 : {
4785 0 : fprintf (out, "sarif_buffered_sink");
4786 0 : }
4787 0 : bool machine_readable_stderr_p () const final override
4788 : {
4789 0 : return false;
4790 : }
4791 168 : std::unique_ptr<sarif_log> flush_to_object ()
4792 : {
4793 168 : return m_builder.flush_to_object ();
4794 : }
4795 : };
4796 :
4797 : sarif_buffered_sink *m_sink; // borrowed
4798 : };
4799 :
4800 : /* Test making a sarif_location for a complex rich_location
4801 : with labels and escape-on-output. */
4802 :
4803 : static void
4804 192 : test_make_location_object (const sarif_generation_options &sarif_gen_opts,
4805 : const ::selftest::line_table_case &case_)
4806 : {
4807 192 : source_printing_fixture_one_liner_utf8 f (case_);
4808 192 : location_t line_end = linemap_position_for_column (line_table, 31);
4809 :
4810 : /* Don't attempt to run the tests if column data might be unavailable. */
4811 192 : if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
4812 64 : return;
4813 :
4814 128 : test_context dc;
4815 128 : pretty_printer pp;
4816 128 : sarif_builder builder
4817 : (dc, pp, line_table,
4818 128 : std::make_unique<sarif_serialization_format_json> (true),
4819 128 : sarif_gen_opts);
4820 :
4821 : /* These "columns" are byte offsets, whereas later on the columns
4822 : in the generated SARIF use sarif_builder::get_sarif_column and
4823 : thus respect tabs, encoding. */
4824 128 : const location_t foo
4825 128 : = make_location (linemap_position_for_column (line_table, 1),
4826 : linemap_position_for_column (line_table, 1),
4827 : linemap_position_for_column (line_table, 8));
4828 128 : const location_t bar
4829 128 : = make_location (linemap_position_for_column (line_table, 12),
4830 : linemap_position_for_column (line_table, 12),
4831 : linemap_position_for_column (line_table, 17));
4832 128 : const location_t field
4833 128 : = make_location (linemap_position_for_column (line_table, 19),
4834 : linemap_position_for_column (line_table, 19),
4835 : linemap_position_for_column (line_table, 30));
4836 :
4837 128 : text_range_label label0 ("label0");
4838 128 : text_range_label label1 ("label1");
4839 128 : text_range_label label2 ("label2");
4840 :
4841 128 : rich_location richloc (line_table, foo, &label0, nullptr);
4842 128 : richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1);
4843 128 : richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2);
4844 128 : richloc.set_escape_on_output (true);
4845 :
4846 128 : sarif_result result (0);
4847 :
4848 128 : std::unique_ptr<sarif_location> location_obj
4849 : = builder.make_location_object
4850 128 : (&result, richloc, logical_locations::key (),
4851 128 : diagnostic_artifact_role::analysis_target);
4852 128 : ASSERT_NE (location_obj, nullptr);
4853 :
4854 128 : auto physical_location
4855 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (location_obj.get (),
4856 : "physicalLocation");
4857 128 : {
4858 128 : auto region
4859 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location, "region");
4860 128 : ASSERT_JSON_INT_PROPERTY_EQ (region, "startLine", 1);
4861 128 : ASSERT_JSON_INT_PROPERTY_EQ (region, "startColumn", 1);
4862 128 : ASSERT_JSON_INT_PROPERTY_EQ (region, "endColumn", 7);
4863 : }
4864 128 : {
4865 128 : auto context_region
4866 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location,
4867 : "contextRegion");
4868 128 : ASSERT_JSON_INT_PROPERTY_EQ (context_region, "startLine", 1);
4869 :
4870 128 : {
4871 128 : auto snippet
4872 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (context_region, "snippet");
4873 :
4874 : /* We expect the snippet's "text" to be a copy of the content. */
4875 128 : ASSERT_JSON_STRING_PROPERTY_EQ (snippet, "text", f.m_content);
4876 :
4877 : /* We expect the snippet to have a "rendered" whose "text" has a
4878 : pure ASCII escaped copy of the line (with labels, etc). */
4879 128 : {
4880 128 : auto rendered
4881 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (snippet, "rendered");
4882 128 : ASSERT_JSON_STRING_PROPERTY_EQ
4883 : (rendered, "text",
4884 : "1 | <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n"
4885 : " | ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n"
4886 : " | | | |\n"
4887 : " | label0 label1 label2\n");
4888 : }
4889 : }
4890 : }
4891 128 : auto annotations
4892 128 : = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (location_obj.get (),
4893 : "annotations");
4894 128 : ASSERT_EQ (annotations->size (), 3);
4895 128 : {
4896 128 : {
4897 128 : auto a0 = (*annotations)[0];
4898 128 : ASSERT_JSON_INT_PROPERTY_EQ (a0, "startLine", 1);
4899 128 : ASSERT_JSON_INT_PROPERTY_EQ (a0, "startColumn", 1);
4900 128 : ASSERT_JSON_INT_PROPERTY_EQ (a0, "endColumn", 7);
4901 128 : auto message
4902 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (a0, "message");
4903 128 : ASSERT_JSON_STRING_PROPERTY_EQ (message, "text", "label0");
4904 : }
4905 128 : {
4906 128 : auto a1 = (*annotations)[1];
4907 128 : ASSERT_JSON_INT_PROPERTY_EQ (a1, "startLine", 1);
4908 128 : ASSERT_JSON_INT_PROPERTY_EQ (a1, "startColumn", 10);
4909 128 : ASSERT_JSON_INT_PROPERTY_EQ (a1, "endColumn", 15);
4910 128 : auto message
4911 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (a1, "message");
4912 128 : ASSERT_JSON_STRING_PROPERTY_EQ (message, "text", "label1");
4913 : }
4914 128 : {
4915 128 : auto a2 = (*annotations)[2];
4916 128 : ASSERT_JSON_INT_PROPERTY_EQ (a2, "startLine", 1);
4917 128 : ASSERT_JSON_INT_PROPERTY_EQ (a2, "startColumn", 16);
4918 128 : ASSERT_JSON_INT_PROPERTY_EQ (a2, "endColumn", 25);
4919 128 : auto message
4920 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (a2, "message");
4921 128 : ASSERT_JSON_STRING_PROPERTY_EQ (message, "text", "label2");
4922 : }
4923 : }
4924 192 : }
4925 :
4926 : /* Test of reporting a diagnostic at UNKNOWN_LOCATION to a
4927 : diagnostics::context and examining the generated sarif_log.
4928 : Verify various basic properties. */
4929 :
4930 : static void
4931 8 : test_simple_log (const sarif_generation_options &sarif_gen_opts)
4932 : {
4933 8 : test_sarif_diagnostic_context dc ("MAIN_INPUT_FILENAME", sarif_gen_opts);
4934 :
4935 8 : rich_location richloc (line_table, UNKNOWN_LOCATION);
4936 8 : dc.report (kind::error, richloc, nullptr, 0, "this is a test: %i", 42);
4937 :
4938 8 : auto log_ptr = dc.flush_to_object ();
4939 :
4940 : // 3.13 sarifLog:
4941 8 : auto log = log_ptr.get ();
4942 8 : const enum sarif_version version = sarif_gen_opts.m_version;
4943 8 : ASSERT_JSON_STRING_PROPERTY_EQ (log, "$schema",
4944 : sarif_version_to_url (version));
4945 8 : ASSERT_JSON_STRING_PROPERTY_EQ (log, "version",
4946 : sarif_version_to_property (version));
4947 :
4948 8 : auto runs = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log, "runs"); // 3.13.4
4949 8 : ASSERT_EQ (runs->size (), 1);
4950 :
4951 : // 3.14 "run" object:
4952 8 : auto run = (*runs)[0];
4953 :
4954 8 : {
4955 : // 3.14.6:
4956 8 : auto tool = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (run, "tool");
4957 :
4958 8 : EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (tool, "driver"); // 3.18.2
4959 : }
4960 :
4961 8 : {
4962 : // 3.14.11
4963 8 : auto invocations
4964 8 : = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run, "invocations");
4965 8 : ASSERT_EQ (invocations->size (), 1);
4966 :
4967 8 : {
4968 : // 3.20 "invocation" object:
4969 8 : auto invocation = (*invocations)[0];
4970 :
4971 : // 3.20.3 arguments property
4972 :
4973 : // 3.20.7 startTimeUtc property
4974 8 : EXPECT_JSON_OBJECT_WITH_STRING_PROPERTY (invocation, "startTimeUtc");
4975 :
4976 : // 3.20.8 endTimeUtc property
4977 8 : EXPECT_JSON_OBJECT_WITH_STRING_PROPERTY (invocation, "endTimeUtc");
4978 :
4979 : // 3.20.19 workingDirectory property
4980 8 : {
4981 8 : auto wd_obj
4982 8 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (invocation,
4983 : "workingDirectory");
4984 8 : EXPECT_JSON_OBJECT_WITH_STRING_PROPERTY (wd_obj, "uri");
4985 : }
4986 :
4987 : // 3.20.21 toolExecutionNotifications property
4988 8 : auto notifications
4989 8 : = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY
4990 : (invocation, "toolExecutionNotifications");
4991 8 : ASSERT_EQ (notifications->size (), 0);
4992 : }
4993 : }
4994 :
4995 8 : {
4996 : // 3.14.15:
4997 8 : auto artifacts = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run, "artifacts");
4998 8 : ASSERT_EQ (artifacts->size (), 1);
4999 :
5000 8 : {
5001 : // 3.24 "artifact" object:
5002 8 : auto artifact = (*artifacts)[0];
5003 :
5004 : // 3.24.2:
5005 8 : auto location
5006 8 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (artifact, "location");
5007 8 : ASSERT_JSON_STRING_PROPERTY_EQ (location, "uri", "MAIN_INPUT_FILENAME");
5008 :
5009 : // 3.24.6:
5010 8 : auto roles = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (artifact, "roles");
5011 8 : ASSERT_EQ (roles->size (), 1);
5012 8 : {
5013 8 : auto role = (*roles)[0];
5014 8 : ASSERT_JSON_STRING_EQ (role, "analysisTarget");
5015 : }
5016 : }
5017 : }
5018 :
5019 8 : {
5020 : // 3.14.23:
5021 8 : auto results = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run, "results");
5022 8 : ASSERT_EQ (results->size (), 1);
5023 :
5024 8 : {
5025 : // 3.27 "result" object:
5026 8 : auto result = (*results)[0];
5027 8 : ASSERT_JSON_STRING_PROPERTY_EQ (result, "ruleId", "error");
5028 8 : ASSERT_JSON_STRING_PROPERTY_EQ (result, "level", "error"); // 3.27.10
5029 :
5030 8 : {
5031 : // 3.27.11:
5032 8 : auto message
5033 8 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result, "message");
5034 8 : ASSERT_JSON_STRING_PROPERTY_EQ (message, "text",
5035 : "this is a test: 42");
5036 : }
5037 :
5038 : // 3.27.12:
5039 8 : auto locations
5040 8 : = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (result, "locations");
5041 8 : ASSERT_EQ (locations->size (), 0);
5042 : }
5043 : }
5044 8 : }
5045 :
5046 : /* As above, but with a "real" location_t. */
5047 :
5048 : static void
5049 192 : test_simple_log_2 (const sarif_generation_options &sarif_gen_opts,
5050 : const line_table_case &case_)
5051 : {
5052 192 : auto_fix_quotes fix_quotes;
5053 :
5054 192 : const char *const content
5055 : /* 000000000111111
5056 : 123456789012345. */
5057 : = "unsinged int i;\n";
5058 192 : source_printing_fixture f (case_, content);
5059 192 : location_t line_end = linemap_position_for_column (line_table, 31);
5060 :
5061 : /* Don't attempt to run the tests if column data might be unavailable. */
5062 192 : if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS)
5063 64 : return;
5064 :
5065 128 : test_sarif_diagnostic_context dc (f.get_filename (), sarif_gen_opts);
5066 :
5067 128 : const location_t typo_loc
5068 128 : = make_location (linemap_position_for_column (line_table, 1),
5069 : linemap_position_for_column (line_table, 1),
5070 : linemap_position_for_column (line_table, 8));
5071 :
5072 128 : rich_location richloc (line_table, typo_loc);
5073 128 : dc.report (kind::error, richloc, nullptr, 0,
5074 : "did you misspell %qs again?",
5075 : "unsigned");
5076 :
5077 128 : auto log_ptr = dc.flush_to_object ();
5078 :
5079 : // 3.13 sarifLog:
5080 128 : auto log = log_ptr.get ();
5081 :
5082 128 : auto runs = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log, "runs"); // 3.13.4
5083 128 : ASSERT_EQ (runs->size (), 1);
5084 :
5085 : // 3.14 "run" object:
5086 128 : auto run = (*runs)[0];
5087 :
5088 128 : {
5089 : // 3.14.23:
5090 128 : auto results = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run, "results");
5091 128 : ASSERT_EQ (results->size (), 1);
5092 :
5093 128 : {
5094 : // 3.27 "result" object:
5095 128 : auto result = (*results)[0];
5096 128 : ASSERT_JSON_STRING_PROPERTY_EQ (result, "ruleId", "error");
5097 128 : ASSERT_JSON_STRING_PROPERTY_EQ (result, "level", "error"); // 3.27.10
5098 :
5099 128 : {
5100 : // 3.27.11:
5101 128 : auto message
5102 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result, "message");
5103 128 : ASSERT_JSON_STRING_PROPERTY_EQ (message, "text",
5104 : "did you misspell `unsigned' again?");
5105 : }
5106 :
5107 : // 3.27.12:
5108 128 : auto locations
5109 128 : = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (result, "locations");
5110 128 : ASSERT_EQ (locations->size (), 1);
5111 :
5112 128 : {
5113 : // 3.28 "location" object:
5114 128 : auto location = (*locations)[0];
5115 :
5116 128 : auto physical_location
5117 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (location,
5118 : "physicalLocation");
5119 128 : {
5120 128 : auto region
5121 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location,
5122 : "region");
5123 128 : ASSERT_JSON_INT_PROPERTY_EQ (region, "startLine", 1);
5124 128 : ASSERT_JSON_INT_PROPERTY_EQ (region, "startColumn", 1);
5125 128 : ASSERT_JSON_INT_PROPERTY_EQ (region, "endColumn", 9);
5126 : }
5127 128 : {
5128 128 : auto context_region
5129 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location,
5130 : "contextRegion");
5131 128 : ASSERT_JSON_INT_PROPERTY_EQ (context_region, "startLine", 1);
5132 :
5133 128 : {
5134 128 : auto snippet
5135 128 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (context_region,
5136 : "snippet");
5137 :
5138 : /* We expect the snippet's "text" to be a copy of the content. */
5139 128 : ASSERT_JSON_STRING_PROPERTY_EQ (snippet, "text", f.m_content);
5140 : }
5141 : }
5142 : }
5143 : }
5144 : }
5145 192 : }
5146 :
5147 : /* Assuming that a single diagnostic has been emitted within
5148 : LOG, get a json::object for the result object. */
5149 :
5150 : static const json::object *
5151 32 : get_result_from_log (const sarif_log *log)
5152 : {
5153 32 : auto runs = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log, "runs"); // 3.13.4
5154 32 : ASSERT_EQ (runs->size (), 1);
5155 :
5156 : // 3.14 "run" object:
5157 32 : auto run = (*runs)[0];
5158 :
5159 : // 3.14.23:
5160 32 : auto results = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run, "results");
5161 32 : ASSERT_EQ (results->size (), 1);
5162 :
5163 : // 3.27 "result" object:
5164 32 : auto result = (*results)[0];
5165 32 : return expect_json_object (SELFTEST_LOCATION, result);
5166 : }
5167 :
5168 : static const json::object *
5169 16 : get_message_from_result (const sarif_result &result)
5170 : {
5171 : // 3.27.11:
5172 16 : auto message_obj
5173 16 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (&result, "message");
5174 16 : return message_obj;
5175 : }
5176 :
5177 : /* Assuming that a single diagnostic has been emitted to
5178 : DC, get a json::object for the messsage object within
5179 : the result. */
5180 :
5181 : static const json::object *
5182 32 : get_message_from_log (const sarif_log *log)
5183 : {
5184 32 : auto result_obj = get_result_from_log (log);
5185 :
5186 : // 3.27.11:
5187 32 : auto message_obj
5188 32 : = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result_obj, "message");
5189 32 : return message_obj;
5190 : }
5191 :
5192 : /* Tests of messages with embedded links; see SARIF v2.1.0 3.11.6. */
5193 :
5194 : static void
5195 8 : test_message_with_embedded_link (const sarif_generation_options &sarif_gen_opts)
5196 : {
5197 8 : auto_fix_quotes fix_quotes;
5198 8 : {
5199 8 : test_sarif_diagnostic_context dc ("test.c", sarif_gen_opts);
5200 8 : rich_location richloc (line_table, UNKNOWN_LOCATION);
5201 8 : dc.report (kind::error, richloc, nullptr, 0,
5202 : "before %{text%} after",
5203 : "http://example.com");
5204 8 : std::unique_ptr<sarif_log> log = dc.flush_to_object ();
5205 :
5206 8 : auto message_obj = get_message_from_log (log.get ());
5207 8 : ASSERT_JSON_STRING_PROPERTY_EQ
5208 : (message_obj, "text",
5209 : "before [text](http://example.com) after");
5210 8 : }
5211 :
5212 : /* Escaping in message text.
5213 : This is "EXAMPLE 1" from 3.11.6. */
5214 8 : {
5215 8 : test_sarif_diagnostic_context dc ("test.c", sarif_gen_opts);
5216 8 : rich_location richloc (line_table, UNKNOWN_LOCATION);
5217 :
5218 : /* Disable "unquoted sequence of 2 consecutive punctuation
5219 : characters `]\' in format" warning. */
5220 : #if __GNUC__ >= 10
5221 8 : # pragma GCC diagnostic push
5222 8 : # pragma GCC diagnostic ignored "-Wformat-diag"
5223 : #endif
5224 8 : dc.report (kind::error, richloc, nullptr, 0,
5225 : "Prohibited term used in %{para[0]\\spans[2]%}.",
5226 : "1");
5227 : #if __GNUC__ >= 10
5228 8 : # pragma GCC diagnostic pop
5229 : #endif
5230 :
5231 8 : std::unique_ptr<sarif_log> log = dc.flush_to_object ();
5232 :
5233 8 : auto message_obj = get_message_from_log (log.get ());
5234 8 : ASSERT_JSON_STRING_PROPERTY_EQ
5235 : (message_obj, "text",
5236 : "Prohibited term used in [para\\[0\\]\\\\spans\\[2\\]](1).");
5237 : /* This isn't exactly what EXAMPLE 1 of the spec has; reported as
5238 : https://github.com/oasis-tcs/sarif-spec/issues/656 */
5239 8 : }
5240 :
5241 : /* Urlifier. */
5242 8 : {
5243 8 : class test_urlifier : public urlifier
5244 : {
5245 : public:
5246 : char *
5247 16 : get_url_for_quoted_text (const char *p, size_t sz) const final override
5248 : {
5249 16 : if (!strncmp (p, "-foption", sz))
5250 8 : return xstrdup ("http://example.com");
5251 : return nullptr;
5252 : }
5253 : };
5254 :
5255 8 : test_sarif_diagnostic_context dc ("test.c", sarif_gen_opts);
5256 8 : dc.push_owned_urlifier (std::make_unique<test_urlifier> ());
5257 8 : rich_location richloc (line_table, UNKNOWN_LOCATION);
5258 8 : dc.report (kind::error, richloc, nullptr, 0,
5259 : "foo %<-foption%> %<unrecognized%> bar");
5260 8 : std::unique_ptr<sarif_log> log = dc.flush_to_object ();
5261 :
5262 8 : auto message_obj = get_message_from_log (log.get ());
5263 8 : ASSERT_JSON_STRING_PROPERTY_EQ
5264 : (message_obj, "text",
5265 : "foo `[-foption](http://example.com)' `unrecognized' bar");
5266 8 : }
5267 8 : }
5268 :
5269 : /* Verify that braces in messages get escaped, as per
5270 : 3.11.5 ("Messages with placeholders"). */
5271 :
5272 : static void
5273 8 : test_message_with_braces (const sarif_generation_options &sarif_gen_opts)
5274 : {
5275 8 : auto_fix_quotes fix_quotes;
5276 8 : {
5277 8 : test_sarif_diagnostic_context dc ("test.c", sarif_gen_opts);
5278 8 : rich_location richloc (line_table, UNKNOWN_LOCATION);
5279 8 : dc.report (kind::error, richloc, nullptr, 0,
5280 : "open brace: %qs close brace: %qs",
5281 : "{", "}");
5282 8 : std::unique_ptr<sarif_log> log = dc.flush_to_object ();
5283 :
5284 8 : auto message_obj = get_message_from_log (log.get ());
5285 8 : ASSERT_JSON_STRING_PROPERTY_EQ
5286 : (message_obj, "text",
5287 : "open brace: `{{' close brace: `}}'");
5288 8 : }
5289 8 : }
5290 :
5291 : static void
5292 8 : test_buffering (const sarif_generation_options &sarif_gen_opts)
5293 : {
5294 8 : test_sarif_diagnostic_context dc ("test.c", sarif_gen_opts);
5295 :
5296 8 : diagnostics::buffer buf_a (dc);
5297 8 : diagnostics::buffer buf_b (dc);
5298 :
5299 8 : rich_location rich_loc (line_table, UNKNOWN_LOCATION);
5300 :
5301 8 : ASSERT_EQ (dc.diagnostic_count (kind::error), 0);
5302 8 : ASSERT_EQ (buf_a.diagnostic_count (kind::error), 0);
5303 8 : ASSERT_EQ (buf_b.diagnostic_count (kind::error), 0);
5304 8 : ASSERT_EQ (dc.num_results (), 0);
5305 8 : ASSERT_TRUE (buf_a.empty_p ());
5306 8 : ASSERT_TRUE (buf_b.empty_p ());
5307 :
5308 : /* Unbuffered diagnostic. */
5309 8 : {
5310 8 : dc.report (kind::error, rich_loc, nullptr, 0,
5311 : "message 1");
5312 :
5313 8 : ASSERT_EQ (dc.diagnostic_count (kind::error), 1);
5314 8 : ASSERT_EQ (buf_a.diagnostic_count (kind::error), 0);
5315 8 : ASSERT_EQ (buf_b.diagnostic_count (kind::error), 0);
5316 8 : ASSERT_EQ (dc.num_results (), 1);
5317 8 : sarif_result &result_obj = dc.get_result (0);
5318 8 : auto message_obj = get_message_from_result (result_obj);
5319 8 : ASSERT_JSON_STRING_PROPERTY_EQ (message_obj, "text",
5320 : "message 1");
5321 8 : ASSERT_TRUE (buf_a.empty_p ());
5322 8 : ASSERT_TRUE (buf_b.empty_p ());
5323 : }
5324 :
5325 : /* Buffer diagnostic into buffer A. */
5326 8 : {
5327 8 : dc.set_diagnostic_buffer (&buf_a);
5328 8 : dc.report (kind::error, rich_loc, nullptr, 0,
5329 : "message in buffer a");
5330 8 : ASSERT_EQ (dc.diagnostic_count (kind::error), 1);
5331 8 : ASSERT_EQ (buf_a.diagnostic_count (kind::error), 1);
5332 8 : ASSERT_EQ (buf_b.diagnostic_count (kind::error), 0);
5333 8 : ASSERT_EQ (dc.num_results (), 1);
5334 8 : ASSERT_FALSE (buf_a.empty_p ());
5335 8 : ASSERT_TRUE (buf_b.empty_p ());
5336 : }
5337 :
5338 : /* Buffer diagnostic into buffer B. */
5339 8 : {
5340 8 : dc.set_diagnostic_buffer (&buf_b);
5341 8 : dc.report (kind::error, rich_loc, nullptr, 0,
5342 : "message in buffer b");
5343 8 : ASSERT_EQ (dc.diagnostic_count (kind::error), 1);
5344 8 : ASSERT_EQ (buf_a.diagnostic_count (kind::error), 1);
5345 8 : ASSERT_EQ (buf_b.diagnostic_count (kind::error), 1);
5346 8 : ASSERT_EQ (dc.num_results (), 1);
5347 8 : ASSERT_FALSE (buf_a.empty_p ());
5348 8 : ASSERT_FALSE (buf_b.empty_p ());
5349 : }
5350 :
5351 : /* Flush buffer B to dc. */
5352 8 : {
5353 8 : dc.flush_diagnostic_buffer (buf_b);
5354 8 : ASSERT_EQ (dc.diagnostic_count (kind::error), 2);
5355 8 : ASSERT_EQ (buf_a.diagnostic_count (kind::error), 1);
5356 8 : ASSERT_EQ (buf_b.diagnostic_count (kind::error), 0);
5357 8 : ASSERT_EQ (dc.num_results (), 2);
5358 8 : sarif_result &result_1_obj = dc.get_result (1);
5359 8 : auto message_1_obj = get_message_from_result (result_1_obj);
5360 8 : ASSERT_JSON_STRING_PROPERTY_EQ (message_1_obj, "text",
5361 : "message in buffer b");
5362 8 : ASSERT_FALSE (buf_a.empty_p ());
5363 8 : ASSERT_TRUE (buf_b.empty_p ());
5364 : }
5365 :
5366 : /* Clear buffer A. */
5367 8 : {
5368 8 : dc.clear_diagnostic_buffer (buf_a);
5369 8 : ASSERT_EQ (dc.diagnostic_count (kind::error), 2);
5370 8 : ASSERT_EQ (buf_a.diagnostic_count (kind::error), 0);
5371 8 : ASSERT_EQ (buf_b.diagnostic_count (kind::error), 0);
5372 8 : ASSERT_EQ (dc.num_results (), 2);
5373 8 : ASSERT_TRUE (buf_a.empty_p ());
5374 8 : ASSERT_TRUE (buf_b.empty_p ());
5375 : }
5376 8 : }
5377 :
5378 : template <class ...ArgTypes>
5379 : static void
5380 208 : for_each_sarif_gen_option (void (*callback) (const sarif_generation_options &,
5381 : ArgTypes ...),
5382 : ArgTypes ...args)
5383 : {
5384 208 : sarif_generation_options sarif_gen_opts;
5385 624 : for (int version_idx = 0;
5386 624 : version_idx < (int)sarif_version::num_versions;
5387 : ++version_idx)
5388 : {
5389 416 : sarif_gen_opts.m_version = static_cast<enum sarif_version> (version_idx);
5390 :
5391 416 : callback (sarif_gen_opts, args...);
5392 : }
5393 208 : }
5394 :
5395 : static void
5396 96 : run_line_table_case_tests_per_version (const line_table_case &case_)
5397 : {
5398 96 : for_each_sarif_gen_option<const line_table_case &>
5399 96 : (test_make_location_object, case_);
5400 :
5401 96 : for_each_sarif_gen_option<const line_table_case &>
5402 96 : (test_simple_log_2, case_);
5403 96 : }
5404 :
5405 : /* Run all of the selftests within this file. */
5406 :
5407 : void
5408 4 : sarif_sink_cc_tests ()
5409 : {
5410 4 : test_sarif_array_of_unique_1 ();
5411 4 : test_sarif_array_of_unique_2 ();
5412 :
5413 4 : for_each_sarif_gen_option (test_simple_log);
5414 4 : for_each_sarif_gen_option (test_message_with_embedded_link);
5415 4 : for_each_sarif_gen_option (test_message_with_braces);
5416 4 : for_each_sarif_gen_option (test_buffering);
5417 :
5418 : /* Run tests per (SARIF gen-option, line-table-case) pair. */
5419 4 : for_each_line_table_case (run_line_table_case_tests_per_version);
5420 4 : }
5421 :
5422 : } // namespace diagnostics::selftest
5423 :
5424 : #endif /* CHECKING_P */
5425 :
5426 : } // namespace diagnostics
|