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