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