diff options
Diffstat (limited to 'gcc/diagnostic-format-sarif.cc')
-rw-r--r-- | gcc/diagnostic-format-sarif.cc | 4853 |
1 files changed, 0 insertions, 4853 deletions
diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc deleted file mode 100644 index 454eaae..0000000 --- a/gcc/diagnostic-format-sarif.cc +++ /dev/null @@ -1,4853 +0,0 @@ -/* SARIF output for diagnostics - Copyright (C) 2018-2025 Free Software Foundation, Inc. - Contributed by David Malcolm <dmalcolm@redhat.com>. - -This file is part of GCC. - -GCC is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free -Software Foundation; either version 3, or (at your option) any later -version. - -GCC is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or -FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -for more details. - -You should have received a copy of the GNU General Public License -along with GCC; see the file COPYING3. If not see -<http://www.gnu.org/licenses/>. */ - - -#include "config.h" -#define INCLUDE_LIST -#define INCLUDE_MAP -#define INCLUDE_STRING -#define INCLUDE_VECTOR -#include "system.h" -#include "coretypes.h" -#include "diagnostic.h" -#include "diagnostic-metadata.h" -#include "diagnostic-path.h" -#include "diagnostic-format.h" -#include "diagnostic-buffer.h" -#include "json.h" -#include "cpplib.h" -#include "logical-location.h" -#include "diagnostic-client-data-hooks.h" -#include "diagnostic-diagram.h" -#include "text-art/canvas.h" -#include "diagnostic-format-sarif.h" -#include "diagnostic-format-text.h" -#include "ordered-hash-map.h" -#include "sbitmap.h" -#include "selftest.h" -#include "selftest-diagnostic.h" -#include "selftest-diagnostic-show-locus.h" -#include "selftest-json.h" -#include "text-range-label.h" -#include "pretty-print-format-impl.h" -#include "pretty-print-urlifier.h" -#include "demangle.h" -#include "backtrace.h" - -/* A json::array where the values are "unique" as per - SARIF v2.1.0 section 3.7.3 ("Array properties with unique values"). */ - -template <typename JsonElementType> -class sarif_array_of_unique : public json::array -{ - public: - size_t append_uniquely (std::unique_ptr<JsonElementType> val) - { - /* This should be O(log(n)) due to the std::map. */ - auto search = m_index_by_value.find (val.get ()); - if (search != m_index_by_value.end()) - return (*search).second; - - const size_t insertion_idx = size (); - m_index_by_value.insert ({val.get (), insertion_idx}); - append (std::move (val)); - return insertion_idx; - } - - /* For ease of reading output, add "index": idx to all - objects in the array. - We don't do this until we've added everything, since - the "index" property would otherwise confuse the - comparison against new elements. */ - void add_explicit_index_values () - { - for (size_t idx = 0; idx < length (); ++idx) - if (json::object *obj = get (idx)->dyn_cast_object ()) - obj->set_integer ("index", idx); - } - -private: - struct comparator_t { - bool operator () (const json::value *a, const json::value *b) const - { - gcc_assert (a); - gcc_assert (b); - return json::value::compare (*a, *b) < 0; - } - }; - - // json::value * here is borrowed from m_elements - std::map<json::value *, int, comparator_t> m_index_by_value; -}; - -/* Forward decls. */ -class sarif_builder; -class content_renderer; - class escape_nonascii_renderer; - -/* Subclasses of sarif_object. - Keep these in order of their descriptions in the specification. */ -class sarif_artifact_content; // 3.3 -class sarif_artifact_location; // 3.4 -class sarif_message; // 3.11 -class sarif_multiformat_message_string; // 3.12 -class sarif_log; // 3.13 -class sarif_run; // 3.14 -class sarif_tool; // 3.18 -class sarif_tool_component; // 3.19 -class sarif_invocation; // 3.20 -class sarif_artifact; // 3.24 -class sarif_location_manager; // not in the spec -class sarif_result; // 3.27 -class sarif_location; // 3.28 -class sarif_physical_location; // 3.29 -class sarif_region; // 3.30 -class sarif_logical_location; // 3.33 -class sarif_location_relationship; // 3.34 -class sarif_code_flow; // 3.36 -class sarif_thread_flow; // 3.37 -class sarif_thread_flow_location; // 3.38 -class sarif_reporting_descriptor; // 3.49 -class sarif_reporting_descriptor_reference; // 3.53 -class sarif_tool_component_reference; // 3.54 -class sarif_fix; // 3.55 -class sarif_artifact_change; // 3.56 -class sarif_replacement; // 3.57 -class sarif_ice_notification; // 3.58 - -// Valid values for locationRelationship's "kinds" property (3.34.3) - -enum class location_relationship_kind -{ - includes, - is_included_by, - relevant, - - NUM_KINDS -}; - -/* Declarations of subclasses of sarif_object. - Keep these in order of their descriptions in the specification. */ - -/* Subclass of sarif_object for SARIF "artifactContent" objects - (SARIF v2.1.0 section 3.3). */ - -class sarif_artifact_content : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "artifactLocation" objects - (SARIF v2.1.0 section 3.4). */ - -class sarif_artifact_location : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "message" objects - (SARIF v2.1.0 section 3.11). */ - -class sarif_message : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "multiformatMessageString" objects - (SARIF v2.1.0 section 3.12). */ - -class sarif_multiformat_message_string : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "log" objects - (SARIF v2.1.0 section 3.13). */ - -class sarif_log : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "run" objects - (SARIF v2.1.0 section 3.14). */ - -class sarif_run : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "tool" objects - (SARIF v2.1.0 section 3.18). */ - -class sarif_tool : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "toolComponent" objects - (SARIF v2.1.0 section 3.19). */ - -class sarif_tool_component : public sarif_object {}; - -/* Make a JSON string for the current date and time. - See SARIF v2.1.0 section 3.9 "Date/time properties". - Given that we don't run at the very beginning/end of the - process, it doesn't make sense to be more accurate than - the current second. */ - -static std::unique_ptr<json::string> -make_date_time_string_for_current_time () -{ - time_t t = time (nullptr); - struct tm *tm = gmtime (&t); - char buf[256]; - snprintf (buf, sizeof (buf) - 1, - ("%04i-%02i-%02iT" - "%02i:%02i:%02iZ"), - tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec); - return std::make_unique<json::string> (buf); -} - -/* Subclass of sarif_object for SARIF "invocation" objects - (SARIF v2.1.0 section 3.20). */ - -class sarif_invocation : public sarif_object -{ -public: - sarif_invocation (sarif_builder &builder, - const char * const *original_argv); - - void add_notification_for_ice (const diagnostic_info &diagnostic, - sarif_builder &builder, - std::unique_ptr<json::object> backtrace); - void prepare_to_flush (sarif_builder &builder); - -private: - std::unique_ptr<json::array> m_notifications_arr; - bool m_success; -}; - -/* Corresponds to values for the SARIF artifact objects "roles" property. - (SARIF v2.1.0 section 3.24.6). */ - -enum class diagnostic_artifact_role -{ - analysis_target, /* "analysisTarget". */ - debug_output_file, /* "debugOutputFile". */ - result_file, /* "resultFile". */ - - /* "scannedFile" added in 2.2; - see https://github.com/oasis-tcs/sarif-spec/issues/459 */ - scanned_file, - - traced_file, /* "tracedFile". */ - - NUM_ROLES -}; - -/* Subclass of sarif_object for SARIF artifact objects - (SARIF v2.1.0 section 3.24). */ - -class sarif_artifact : public sarif_object -{ -public: - sarif_artifact (const char *filename) - : m_filename (filename), - m_roles ((unsigned)diagnostic_artifact_role::NUM_ROLES), - m_embed_contents (false) - { - bitmap_clear (m_roles); - } - - void add_role (enum diagnostic_artifact_role role, - bool embed_contents); - - bool embed_contents_p () const { return m_embed_contents; } - void populate_contents (sarif_builder &builder); - void populate_roles (); - -private: - const char *m_filename; - auto_sbitmap m_roles; - - /* Flag to track whether this artifact should have a "contents" property - (SARIF v2.1.0 section 3.24.8). - We only add the contents for those artifacts that have a location - referencing them (so that a consumer might want to quote the source). */ - bool m_embed_contents; -}; - -/* A class for sarif_objects that own a "namespace" of numeric IDs for - managing location objects within them. Currently (SARIF v2.1.0) - this is just for sarif_result (section 3.28.2), but it will likely - eventually also be for notification objects; see - https://github.com/oasis-tcs/sarif-spec/issues/540 - - Consider locations with chains of include information e.g. - - > include-chain-1.c: - > #include "include-chain-1.h" - - include-chain-1.h: - | // First set of decls, which will be referenced in notes - | #include "include-chain-1-1.h" - | - | // Second set of decls, which will trigger the errors - | #include "include-chain-1-2.h" - - include-chain-1-1.h: - | int p; - | int q; - - include-chain-1-1.h: - | char p; - | char q; - - GCC's textual output emits: - | In file included from PATH/include-chain-1.h:5, - | from PATH/include-chain-1.c:30: - | PATH/include-chain-1-2.h:1:6: error: conflicting types for 'p'; have 'char' - | 1 | char p; - | | ^ - | In file included from PATH/include-chain-1.h:2: - | PATH/include-chain-1-1.h:1:5: note: previous declaration of 'p' with type 'int' - | 1 | int p; - | | ^ - | PATH/include-chain-1-2.h:2:6: error: conflicting types for 'q'; have 'char' - | 2 | char q; - | | ^ - | PATH/include-chain-1-1.h:2:5: note: previous declaration of 'q' with type 'int' - | 2 | int q; - | | ^ - - Whenever a SARIF location is added for a location_t that - was #included from somewhere, we queue up the creation of a SARIF - location for the location of the #include. The worklist of queued - locations is flushed when the result is finished, which lazily creates - any additional related locations for the include chain, and the - relationships between the locations. Doing so can lead to further - include locations being processed. The worklist approach allows us - to lazily explore the relevant part of the directed graph of location_t - values implicit in our line_maps structure, replicating it as a directed - graph of SARIF locations within the SARIF result object, like this: - - [0]: error in include-chain-1-2.h ("conflicting types for 'p'; have 'char'") - [1]: #include "include-chain-1-2.h" in include-chain-1.h - [2]: note in include-chain-1-2.h ("previous declaration of 'p' with type 'int'") - [3]: #include "include-chain-1-1.h" in include-chain-1.h - [4]: #include "include-chain-1.h" in include-chain-1.c - - where we want to capture this "includes" graph in SARIF form: - . +-----------------------------------+ +----------------------------------+ - . |"id": 0 | |"id": 2 | - . | error: "conflicting types for 'p';| | note: previous declaration of 'p'| - . | have 'char'"| | | with type 'int'") | - . | in include-chain-1-2.h | | in include-chain-1-1.h | - . +-----------------------------------+ +----------------------------------+ - . ^ | ^ | - . includes | | included-by includes | | included-by - . | V | V - . +--------------------------------+ +--------------------------------+ - . |"id": 1 | |"id": 3 | - . | #include "include-chain-1-2.h" | | #include "include-chain-1-1.h" | - . | in include-chain-1.h | | in include-chain-1.h | - . +--------------------------------+ +--------------------------------+ - . ^ | ^ | - . includes | | included-by includes | | included-by - . | V | V - . +------------------------------------+ - . |"id": 4 | - . | The #include "include-chain-1.h" | - . | in include-chain-1.c | - . +------------------------------------+ - */ - -class sarif_location_manager : public sarif_object -{ -public: - /* A worklist of pending actions needed to fully process this object. - - This lets us lazily walk our data structures to build the - directed graph of locations, whilst keeping "notes" at the top - of the "relatedLocations" array, and avoiding the need for - recursion. */ - struct worklist_item - { - enum class kind - { - /* Process a #include relationship where m_location_obj - was #included-d at m_where. */ - included_from, - - /* Process a location_t that was added as a secondary location - to a rich_location without a label. */ - unlabelled_secondary_location - }; - - worklist_item (sarif_location &location_obj, - enum kind kind, - location_t where) - : m_location_obj (location_obj), - m_kind (kind), - m_where (where) - { - } - - sarif_location &m_location_obj; - enum kind m_kind; - location_t m_where; - }; - - sarif_location_manager () - : m_related_locations_arr (nullptr), - m_next_location_id (0) - { - } - - unsigned allocate_location_id () - { - return m_next_location_id++; - } - - virtual void - add_related_location (std::unique_ptr<sarif_location> location_obj, - sarif_builder &builder); - - void - add_relationship_to_worklist (sarif_location &location_obj, - enum worklist_item::kind kind, - location_t where); - - void - process_worklist (sarif_builder &builder); - - void - process_worklist_item (sarif_builder &builder, - const worklist_item &item); -private: - json::array *m_related_locations_arr; // borrowed - unsigned m_next_location_id; - - std::list<worklist_item> m_worklist; - std::map<location_t, sarif_location *> m_included_from_locations; - std::map<location_t, sarif_location *> m_unlabelled_secondary_locations; -}; - -/* Subclass of sarif_object for SARIF "result" objects - (SARIF v2.1.0 section 3.27). - Each SARIF result object has its own "namespace" of numeric IDs for - managing location objects (SARIF v2.1.0 section 3.28.2). */ - -class sarif_result : public sarif_location_manager -{ -public: - sarif_result (unsigned idx_within_parent) - : m_idx_within_parent (idx_within_parent) - {} - - unsigned get_index_within_parent () const { return m_idx_within_parent; } - - void - on_nested_diagnostic (const diagnostic_info &diagnostic, - diagnostic_t orig_diag_kind, - sarif_builder &builder); - void on_diagram (const diagnostic_diagram &diagram, - sarif_builder &builder); - -private: - const unsigned m_idx_within_parent; -}; - -/* Subclass of sarif_object for SARIF "location" objects - (SARIF v2.1.0 section 3.28). - A location object can have an "id" which must be unique within - the enclosing result, if any (see SARIF v2.1.0 section 3.28.2). */ - -class sarif_location : public sarif_object -{ -public: - long lazily_add_id (sarif_location_manager &loc_mgr); - long get_id () const; - - void lazily_add_relationship (sarif_location &target, - enum location_relationship_kind kind, - sarif_location_manager &loc_mgr); - -private: - sarif_location_relationship & - lazily_add_relationship_object (sarif_location &target, - sarif_location_manager &loc_mgr); - - json::array &lazily_add_relationships_array (); - - std::map<sarif_location *, - sarif_location_relationship *> m_relationships_map; -}; - -/* Subclass of sarif_object for SARIF "physicalLocation" objects - (SARIF v2.1.0 section 3.29). */ - -class sarif_physical_location : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "region" objects - (SARIF v2.1.0 section 3.30). */ - -class sarif_region : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "logicalLocation" objects - (SARIF v2.1.0 section 3.33). */ - -class sarif_logical_location : public sarif_object -{ -}; - -/* Subclass of sarif_object for SARIF "locationRelationship" objects - (SARIF v2.1.0 section 3.34). */ - -class sarif_location_relationship : public sarif_object -{ -public: - sarif_location_relationship (sarif_location &target, - sarif_location_manager &loc_mgr); - - long get_target_id () const; - - void lazily_add_kind (enum location_relationship_kind kind); - -private: - auto_sbitmap m_kinds; -}; - -/* Subclass of sarif_object for SARIF "codeFlow" objects - (SARIF v2.1.0 section 3.36). */ - -class sarif_code_flow : public sarif_object -{ -public: - sarif_code_flow (sarif_result &parent, - unsigned idx_within_parent); - - sarif_result &get_parent () const { return m_parent; } - unsigned get_index_within_parent () const { return m_idx_within_parent; } - - sarif_thread_flow & - get_or_append_thread_flow (const diagnostic_thread &thread, - diagnostic_thread_id_t thread_id); - - sarif_thread_flow & - get_thread_flow (diagnostic_thread_id_t thread_id); - - void add_location (sarif_thread_flow_location &); - - sarif_thread_flow_location & - get_thread_flow_loc_obj (diagnostic_event_id_t event_id) const; - -private: - sarif_result &m_parent; - const unsigned m_idx_within_parent; - - hash_map<int_hash<diagnostic_thread_id_t, -1, -2>, - sarif_thread_flow *> m_thread_id_map; // borrowed ptr - json::array *m_thread_flows_arr; // borrowed - - /* Vec of borrowed ptr, allowing for going easily from - an event_id to the corresponding threadFlowLocation object. */ - std::vector<sarif_thread_flow_location *> m_all_tfl_objs; -}; - -/* Subclass of sarif_object for SARIF "threadFlow" objects - (SARIF v2.1.0 section 3.37). */ - -class sarif_thread_flow : public sarif_object -{ -public: - sarif_thread_flow (sarif_code_flow &parent, - const diagnostic_thread &thread, - unsigned idx_within_parent); - - sarif_code_flow &get_parent () const { return m_parent; } - unsigned get_index_within_parent () const { return m_idx_within_parent; } - - sarif_thread_flow_location &add_location (); - -private: - sarif_code_flow &m_parent; - json::array *m_locations_arr; // borrowed - const unsigned m_idx_within_parent; -}; - -/* Subclass of sarif_object for SARIF "threadFlowLocation" objects - (SARIF v2.1.0 section 3.38). */ - -class sarif_thread_flow_location : public sarif_object -{ -public: - sarif_thread_flow_location (sarif_thread_flow &parent, - unsigned idx_within_parent) - : m_parent (parent), - m_idx_within_parent (idx_within_parent) - { - } - - sarif_thread_flow &get_parent () const { return m_parent; } - unsigned get_index_within_parent () const { return m_idx_within_parent; } - -private: - sarif_thread_flow &m_parent; - const unsigned m_idx_within_parent; -}; - -/* Subclass of sarif_object for SARIF "reportingDescriptor" objects - (SARIF v2.1.0 section 3.49). */ - -class sarif_reporting_descriptor : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "reportingDescriptorReference" objects - (SARIF v2.1.0 section 3.53). */ - -class sarif_reporting_descriptor_reference : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "toolComponentReference" objects - (SARIF v2.1.0 section 3.54). */ - -class sarif_tool_component_reference : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "fix" objects - (SARIF v2.1.0 section 3.55). */ - -class sarif_fix : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "artifactChange" objects - (SARIF v2.1.0 section 3.56). */ - -class sarif_artifact_change : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "replacement" objects - (SARIF v2.1.0 section 3.57). */ - -class sarif_replacement : public sarif_object {}; - -/* Subclass of sarif_object for SARIF "notification" objects - (SARIF v2.1.0 section 3.58). - - This subclass is specifically for notifying when an - internal compiler error occurs. */ - -class sarif_ice_notification : public sarif_location_manager -{ -public: - sarif_ice_notification (const diagnostic_info &diagnostic, - sarif_builder &builder, - std::unique_ptr<json::object> backtrace); - - void - add_related_location (std::unique_ptr<sarif_location> location_obj, - sarif_builder &builder) final override; -}; - -/* Abstract base class for use when making an "artifactContent" - object (SARIF v2.1.0 section 3.3): generate a value for the - 3.3.4 "rendered" property. - Can return nullptr, for "no property". */ - -class content_renderer -{ -public: - virtual ~content_renderer () {} - - virtual std::unique_ptr<sarif_multiformat_message_string> - render (const sarif_builder &builder) const = 0; -}; - -/* Concrete buffering implementation subclass for JSON output. */ - -class diagnostic_sarif_format_buffer : public diagnostic_per_format_buffer -{ -public: - friend class sarif_output_format; - - diagnostic_sarif_format_buffer (sarif_builder &builder) - : m_builder (builder) - {} - - void dump (FILE *out, int indent) const final override; - bool empty_p () const final override; - void move_to (diagnostic_per_format_buffer &dest) final override; - void clear () final override; - void flush () final override; - - void add_result (std::unique_ptr<sarif_result> result) - { - m_results.push_back (std::move (result)); - } - - size_t num_results () const { return m_results.size (); } - sarif_result &get_result (size_t idx) { return *m_results[idx]; } - -private: - sarif_builder &m_builder; - std::vector<std::unique_ptr<sarif_result>> m_results; -}; - -/* Classes for abstracting away JSON vs other serialization formats. */ - -// class sarif_serialization_format_json : public sarif_serialization_format - -void -sarif_serialization_format_json::write_to_file (FILE *outf, - const json::value &top) -{ - top.dump (outf, m_formatted); - fprintf (outf, "\n"); -} - -/* A class for managing SARIF output (for -fdiagnostics-format=sarif-stderr - and -fdiagnostics-format=sarif-file). - - As diagnostics occur, we build "result" JSON objects, and - accumulate state: - - which source files are referenced - - which warnings are emitted - - which CWEs are used - - At the end of the compile, we use the above to build the full SARIF - object tree, adding the result objects to the correct place, and - creating objects for the various source files, warnings and CWEs - referenced. - - Implemented: - - fix-it hints - - CWE metadata - - diagnostic groups (see limitations below) - - logical locations (e.g. cfun) - - labelled ranges (as annotations) - - secondary ranges without labels (as related locations) - - Known limitations: - - GCC supports nesting of diagnostics (one-deep nesting via - auto_diagnostic_group, and arbitrary nesting via - auto_diagnostic_nesting_level). These are captured in the SARIF - as related locations, and so we only capture location and message - information from such nested diagnostics (e.g. we ignore fix-it - hints on them). Diagnostics within an auto_diagnostic_nesting_level - have their nesting level captured as a property. - - although we capture command-line arguments (section 3.20.2), we don't - yet capture response files. - - doesn't capture "artifact.encoding" property - (SARIF v2.1.0 section 3.24.9). - - doesn't capture hashes of the source files - ("artifact.hashes" property (SARIF v2.1.0 section 3.24.11). - - doesn't capture the "analysisTarget" property - (SARIF v2.1.0 section 3.27.13). - - doesn't capture -Werror cleanly - - doesn't capture inlining information (can SARIF handle this?) - - doesn't capture macro expansion information (can SARIF handle this?). - - doesn't capture any diagnostic_metadata::rules associated with - a diagnostic. */ - -class sarif_builder -{ -public: - friend class diagnostic_sarif_format_buffer; - - sarif_builder (diagnostic_context &context, - pretty_printer &printer, - const line_maps *line_maps, - const char *main_input_filename_, - std::unique_ptr<sarif_serialization_format> serialization_format, - const sarif_generation_options &sarif_gen_opts); - ~sarif_builder (); - - void set_printer (pretty_printer &printer) - { - m_printer = &printer; - } - - const logical_location_manager * - get_logical_location_manager () const - { - return m_logical_loc_mgr; - } - - void on_report_diagnostic (const diagnostic_info &diagnostic, - diagnostic_t orig_diag_kind, - diagnostic_sarif_format_buffer *buffer); - void emit_diagram (const diagnostic_diagram &diagram); - void end_group (); - - std::unique_ptr<sarif_result> take_current_result () - { - return std::move (m_cur_group_result); - } - - std::unique_ptr<sarif_log> flush_to_object (); - void flush_to_file (FILE *outf); - - std::unique_ptr<json::array> - make_locations_arr (sarif_location_manager &loc_mgr, - const diagnostic_info &diagnostic, - enum diagnostic_artifact_role role); - std::unique_ptr<sarif_location> - make_location_object (sarif_location_manager &loc_mgr, - const rich_location &rich_loc, - logical_location logical_loc, - enum diagnostic_artifact_role role); - std::unique_ptr<sarif_location> - make_location_object (sarif_location_manager &loc_mgr, - location_t where, - enum diagnostic_artifact_role role); - std::unique_ptr<sarif_message> - make_message_object (const char *msg) const; - std::unique_ptr<sarif_message> - make_message_object_for_diagram (const diagnostic_diagram &diagram); - std::unique_ptr<sarif_artifact_content> - maybe_make_artifact_content_object (const char *filename) const; - - std::unique_ptr<sarif_artifact_location> - make_artifact_location_object (const char *filename); - - const sarif_code_flow * - get_code_flow_for_event_ids () const - { - return m_current_code_flow; - } - - diagnostic_context &get_context () const { return m_context; } - pretty_printer *get_printer () const { return m_printer; } - token_printer &get_token_printer () { return m_token_printer; } - enum sarif_version get_version () const { return m_sarif_gen_opts.m_version; } - - size_t num_results () const { return m_results_array->size (); } - sarif_result &get_result (size_t idx) - { - auto element = (*m_results_array)[idx]; - gcc_assert (element); - return *static_cast<sarif_result *> (element); - } - - const sarif_generation_options &get_opts () const { return m_sarif_gen_opts; } - - std::unique_ptr<sarif_logical_location> - make_minimal_sarif_logical_location (logical_location); - -private: - class sarif_token_printer : public token_printer - { - public: - sarif_token_printer (sarif_builder &builder) - : m_builder (builder) - { - } - void print_tokens (pretty_printer *pp, - const pp_token_list &tokens) final override; - private: - sarif_builder &m_builder; - }; - - std::unique_ptr<sarif_result> - make_result_object (const diagnostic_info &diagnostic, - diagnostic_t orig_diag_kind, - unsigned idx_within_parent); - void - add_any_include_chain (sarif_location_manager &loc_mgr, - sarif_location &location_obj, - location_t where); - void - set_any_logical_locs_arr (sarif_location &location_obj, - logical_location logical_loc); - std::unique_ptr<sarif_location> - make_location_object (sarif_location_manager &loc_mgr, - const diagnostic_event &event, - enum diagnostic_artifact_role role); - std::unique_ptr<sarif_code_flow> - make_code_flow_object (sarif_result &result, - unsigned idx_within_parent, - const diagnostic_path &path); - void - populate_thread_flow_location_object (sarif_result &result, - sarif_thread_flow_location &thread_flow_loc_obj, - const diagnostic_event &event, - int event_execution_idx); - std::unique_ptr<json::array> - maybe_make_kinds_array (diagnostic_event::meaning m) const; - std::unique_ptr<sarif_physical_location> - maybe_make_physical_location_object (location_t loc, - enum diagnostic_artifact_role role, - int column_override, - const content_renderer *snippet_renderer); - std::unique_ptr<sarif_artifact_location> - make_artifact_location_object (location_t loc); - std::unique_ptr<sarif_artifact_location> - make_artifact_location_object_for_pwd () const; - std::unique_ptr<sarif_region> - maybe_make_region_object (location_t loc, - int column_override) const; - std::unique_ptr<sarif_region> - maybe_make_region_object_for_context (location_t loc, - const content_renderer *snippet_renderer) const; - std::unique_ptr<sarif_region> - make_region_object_for_hint (const fixit_hint &hint) const; - - int - ensure_sarif_logical_location_for (logical_location k); - - std::unique_ptr<sarif_multiformat_message_string> - make_multiformat_message_string (const char *msg) const; - std::unique_ptr<sarif_log> - make_top_level_object (std::unique_ptr<sarif_invocation> invocation_obj, - std::unique_ptr<json::array> results); - std::unique_ptr<sarif_run> - make_run_object (std::unique_ptr<sarif_invocation> invocation_obj, - std::unique_ptr<json::array> results); - std::unique_ptr<sarif_tool> - make_tool_object (); - std::unique_ptr<sarif_tool_component> - make_driver_tool_component_object (); - std::unique_ptr<json::array> maybe_make_taxonomies_array () const; - std::unique_ptr<sarif_tool_component> - maybe_make_cwe_taxonomy_object () const; - std::unique_ptr<sarif_tool_component_reference> - make_tool_component_reference_object_for_cwe () const; - std::unique_ptr<sarif_reporting_descriptor> - make_reporting_descriptor_object_for_warning (const diagnostic_info &diagnostic, - diagnostic_t orig_diag_kind, - const char *option_text); - std::unique_ptr<sarif_reporting_descriptor> - make_reporting_descriptor_object_for_cwe_id (int cwe_id) const; - std::unique_ptr<sarif_reporting_descriptor_reference> - make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id); - sarif_artifact & - get_or_create_artifact (const char *filename, - enum diagnostic_artifact_role role, - bool embed_contents); - char * - get_source_lines (const char *filename, - int start_line, - int end_line) const; - std::unique_ptr<sarif_artifact_content> - maybe_make_artifact_content_object (const char *filename, - int start_line, - int end_line, - const content_renderer *r) const; - std::unique_ptr<sarif_fix> - make_fix_object (const rich_location &rich_loc); - std::unique_ptr<sarif_artifact_change> - make_artifact_change_object (const rich_location &richloc); - std::unique_ptr<sarif_replacement> - make_replacement_object (const fixit_hint &hint) const; - std::unique_ptr<sarif_artifact_content> - make_artifact_content_object (const char *text) const; - int get_sarif_column (expanded_location exploc) const; - - std::unique_ptr<json::object> - make_stack_from_backtrace (); - - diagnostic_context &m_context; - pretty_printer *m_printer; - const line_maps *m_line_maps; - sarif_token_printer m_token_printer; - - const logical_location_manager *m_logical_loc_mgr; - - /* The JSON object for the invocation object. */ - std::unique_ptr<sarif_invocation> m_invocation_obj; - - /* The JSON array of pending diagnostics. */ - std::unique_ptr<json::array> m_results_array; - - /* The JSON object for the result object (if any) in the current - diagnostic group. */ - std::unique_ptr<sarif_result> m_cur_group_result; - - /* Ideally we'd use std::unique_ptr<sarif_artifact> here, but I had - trouble getting this to work when building with GCC 4.8. */ - ordered_hash_map <nofree_string_hash, - sarif_artifact *> m_filename_to_artifact_map; - - bool m_seen_any_relative_paths; - hash_set <free_string_hash> m_rule_id_set; - std::unique_ptr<json::array> m_rules_arr; - - /* The set of all CWE IDs we've seen, if any. */ - hash_set <int_hash <int, 0, 1> > m_cwe_id_set; - - std::unique_ptr<sarif_array_of_unique<sarif_logical_location>> m_cached_logical_locs; - - int m_tabstop; - - std::unique_ptr<sarif_serialization_format> m_serialization_format; - const sarif_generation_options m_sarif_gen_opts; - - unsigned m_next_result_idx; - sarif_code_flow *m_current_code_flow; -}; - -/* class sarif_object : public json::object. */ - -sarif_property_bag & -sarif_object::get_or_create_properties () -{ - json::value *properties_val = get ("properties"); - if (properties_val) - { - if (properties_val->get_kind () == json::JSON_OBJECT) - return *static_cast <sarif_property_bag *> (properties_val); - } - - sarif_property_bag *bag = new sarif_property_bag (); - set ("properties", bag); - return *bag; -} - -/* class sarif_invocation : public sarif_object. */ - -sarif_invocation::sarif_invocation (sarif_builder &builder, - const char * const *original_argv) -: m_notifications_arr (std::make_unique<json::array> ()), - m_success (true) -{ - // "arguments" property (SARIF v2.1.0 section 3.20.2) - if (original_argv) - { - auto arguments_arr = std::make_unique<json::array> (); - for (size_t i = 0; original_argv[i]; ++i) - arguments_arr->append_string (original_argv[i]); - set<json::array> ("arguments", std::move (arguments_arr)); - } - - // "workingDirectory" property (SARIF v2.1.0 section 3.20.19) - if (const char *pwd = getpwd ()) - set<sarif_artifact_location> ("workingDirectory", - builder.make_artifact_location_object (pwd)); - - // "startTimeUtc" property (SARIF v2.1.0 section 3.20.7) - set<json::string> ("startTimeUtc", - make_date_time_string_for_current_time ()); -} - -/* Handle an internal compiler error DIAGNOSTIC. - Add an object representing the ICE to the notifications array. */ - -void -sarif_invocation::add_notification_for_ice (const diagnostic_info &diagnostic, - sarif_builder &builder, - std::unique_ptr<json::object> backtrace) -{ - m_success = false; - - auto notification - = std::make_unique<sarif_ice_notification> (diagnostic, - builder, - std::move (backtrace)); - - /* Support for related locations within a notification was added - in SARIF 2.2; see https://github.com/oasis-tcs/sarif-spec/issues/540 */ - if (builder.get_version () >= sarif_version::v2_2_prerelease_2024_08_08) - notification->process_worklist (builder); - - m_notifications_arr->append<sarif_ice_notification> - (std::move (notification)); -} - -void -sarif_invocation::prepare_to_flush (sarif_builder &builder) -{ - const diagnostic_context &context = builder.get_context (); - - /* "executionSuccessful" property (SARIF v2.1.0 section 3.20.14). */ - if (context.execution_failed_p ()) - m_success = false; - set_bool ("executionSuccessful", m_success); - - /* "toolExecutionNotifications" property (SARIF v2.1.0 section 3.20.21). */ - set ("toolExecutionNotifications", std::move (m_notifications_arr)); - - /* Call client hook, allowing it to create a custom property bag for - this object (SARIF v2.1.0 section 3.8) e.g. for recording time vars. */ - if (auto client_data_hooks = context.get_client_data_hooks ()) - client_data_hooks->add_sarif_invocation_properties (*this); - - // "endTimeUtc" property (SARIF v2.1.0 section 3.20.8); - set<json::string> ("endTimeUtc", - make_date_time_string_for_current_time ()); -} - -/* class sarif_artifact : public sarif_object. */ - -/* Add ROLE to this artifact's roles. - If EMBED_CONTENTS is true, then flag that we will attempt to embed the - contents of this artifact when writing it out. */ - -void -sarif_artifact::add_role (enum diagnostic_artifact_role role, - bool embed_contents) -{ - /* TODO(SARIF 2.2): "scannedFile" is to be added as a role in SARIF 2.2; - see https://github.com/oasis-tcs/sarif-spec/issues/459 - - For now, skip them. - Ultimately, we probably shouldn't bother embedding the contents - of such artifacts, just the snippets. */ - if (role == diagnostic_artifact_role::scanned_file) - return; - - if (embed_contents) - m_embed_contents = true; - - /* In SARIF v2.1.0 section 3.24.6 "roles" property: - "resultFile" is for an artifact - "which the analysis tool was not explicitly instructed to scan", - whereas "analysisTarget" is for one where the - "analysis tool was instructed to scan this artifact". - Hence the latter excludes the former. */ - if (role == diagnostic_artifact_role::result_file) - if (bitmap_bit_p (m_roles, (int)diagnostic_artifact_role::analysis_target)) - return; - - bitmap_set_bit (m_roles, (int)role); -} - -/* Populate the "contents" property (SARIF v2.1.0 section 3.24.8). - We do this after initialization to - (a) ensure that any charset options have been set - (b) only populate it for artifacts that are referenced by a location. */ - -void -sarif_artifact::populate_contents (sarif_builder &builder) -{ - if (auto artifact_content_obj - = builder.maybe_make_artifact_content_object (m_filename)) - set<sarif_artifact_content> ("contents", std::move (artifact_content_obj)); -} - -/* Get a string for ROLE corresponding to the - SARIF v2.1.0 section 3.24.6 "roles" property. */ - -static const char * -get_artifact_role_string (enum diagnostic_artifact_role role) -{ - switch (role) - { - default: - gcc_unreachable (); - case diagnostic_artifact_role::analysis_target: - return "analysisTarget"; - case diagnostic_artifact_role::debug_output_file: - return "debugOutputFile"; - case diagnostic_artifact_role::result_file: - return "resultFile"; - case diagnostic_artifact_role::scanned_file: - return "scannedFile"; - case diagnostic_artifact_role::traced_file: - return "tracedFile"; - } -} - -/* Populate the "roles" property of this sarif_artifact with a new - json::array for the artifact.roles property (SARIF v2.1.0 section 3.24.6) - containing strings such as "analysisTarget", "resultFile" - and/or "tracedFile". */ - -void -sarif_artifact::populate_roles () -{ - if (bitmap_empty_p (m_roles)) - return; - auto roles_arr (std::make_unique<json::array> ()); - for (int i = 0; i < (int)diagnostic_artifact_role::NUM_ROLES; i++) - if (bitmap_bit_p (m_roles, i)) - { - enum diagnostic_artifact_role role = (enum diagnostic_artifact_role)i; - roles_arr->append_string (get_artifact_role_string (role)); - } - set<json::array> ("roles", std::move (roles_arr)); -} - -/* class sarif_location_manager : public sarif_object. */ - -/* Base implementation of sarif_location_manager::add_related_location vfunc. - - Add LOCATION_OBJ to this object's "relatedLocations" array, - creating it if it doesn't yet exist. */ - -void -sarif_location_manager:: -add_related_location (std::unique_ptr<sarif_location> location_obj, - sarif_builder &) -{ - if (!m_related_locations_arr) - { - m_related_locations_arr = new json::array (); - /* Give ownership of m_related_locations_arr to json::object; - keep a borrowed ptr. */ - set ("relatedLocations", m_related_locations_arr); - } - m_related_locations_arr->append (std::move (location_obj)); -} - -void -sarif_location_manager:: -add_relationship_to_worklist (sarif_location &location_obj, - enum worklist_item::kind kind, - location_t where) -{ - m_worklist.push_back (worklist_item (location_obj, - kind, - where)); -} - -/* Process all items in this result's worklist. - Doing so may temporarily add new items to the end - of the worklist. - Handling any item should be "lazy", and thus we should - eventually drain the queue and terminate. */ - -void -sarif_location_manager::process_worklist (sarif_builder &builder) -{ - while (!m_worklist.empty ()) - { - const worklist_item &item = m_worklist.front (); - process_worklist_item (builder, item); - m_worklist.pop_front (); - } -} - -/* Process one item in this result's worklist, potentially - adding new items to the end of the worklist. */ - -void -sarif_location_manager::process_worklist_item (sarif_builder &builder, - const worklist_item &item) -{ - switch (item.m_kind) - { - default: - gcc_unreachable (); - case worklist_item::kind::included_from: - { - sarif_location &included_loc_obj = item.m_location_obj; - sarif_location *includer_loc_obj = nullptr; - auto iter = m_included_from_locations.find (item.m_where); - if (iter != m_included_from_locations.end ()) - includer_loc_obj = iter->second; - else - { - std::unique_ptr<sarif_location> new_loc_obj - = builder.make_location_object - (*this, - item.m_where, - diagnostic_artifact_role::scanned_file); - includer_loc_obj = new_loc_obj.get (); - add_related_location (std::move (new_loc_obj), builder); - auto kv - = std::pair<location_t, sarif_location *> (item.m_where, - includer_loc_obj); - m_included_from_locations.insert (kv); - } - - includer_loc_obj->lazily_add_relationship - (included_loc_obj, - location_relationship_kind::includes, - *this); - included_loc_obj.lazily_add_relationship - (*includer_loc_obj, - location_relationship_kind::is_included_by, - *this); - } - break; - case worklist_item::kind::unlabelled_secondary_location: - { - sarif_location &primary_loc_obj = item.m_location_obj; - sarif_location *secondary_loc_obj = nullptr; - auto iter = m_unlabelled_secondary_locations.find (item.m_where); - if (iter != m_unlabelled_secondary_locations.end ()) - secondary_loc_obj = iter->second; - else - { - std::unique_ptr<sarif_location> new_loc_obj - = builder.make_location_object - (*this, - item.m_where, - diagnostic_artifact_role::scanned_file); - secondary_loc_obj = new_loc_obj.get (); - add_related_location (std::move (new_loc_obj), builder); - auto kv - = std::pair<location_t, sarif_location *> (item.m_where, - secondary_loc_obj); - m_unlabelled_secondary_locations.insert (kv); - } - gcc_assert (secondary_loc_obj); - primary_loc_obj.lazily_add_relationship - (*secondary_loc_obj, - location_relationship_kind::relevant, - *this); - } - break; - } -} - -/* class sarif_result : public sarif_location_manager. */ - -/* Handle secondary diagnostics that occur within a diagnostic group. - The closest SARIF seems to have to nested diagnostics is the - "relatedLocations" property of result objects (SARIF v2.1.0 section 3.27.22), - so we lazily set this property and populate the array if and when - secondary diagnostics occur (such as notes to a warning). */ - -void -sarif_result::on_nested_diagnostic (const diagnostic_info &diagnostic, - diagnostic_t /*orig_diag_kind*/, - sarif_builder &builder) -{ - /* We don't yet generate meaningful logical locations for notes; - sometimes these will related to current_function_decl, but - often they won't. */ - auto location_obj - = builder.make_location_object (*this, *diagnostic.richloc, - logical_location (), - diagnostic_artifact_role::result_file); - auto message_obj - = builder.make_message_object (pp_formatted_text (builder.get_printer ())); - pp_clear_output_area (builder.get_printer ()); - location_obj->set<sarif_message> ("message", std::move (message_obj)); - - /* Add nesting level, as per "P3358R0 SARIF for Structured Diagnostics" - https://wg21.link/P3358R0 */ - sarif_property_bag &bag = location_obj->get_or_create_properties (); - bag.set_integer ("nestingLevel", - builder.get_context ().get_diagnostic_nesting_level ()); - - add_related_location (std::move (location_obj), builder); -} - -/* Handle diagrams that occur within a diagnostic group. - The closest thing in SARIF seems to be to add a location to the - "releatedLocations" property (SARIF v2.1.0 section 3.27.22), - and to put the diagram into the "message" property of that location - (SARIF v2.1.0 section 3.28.5). */ - -void -sarif_result::on_diagram (const diagnostic_diagram &diagram, - sarif_builder &builder) -{ - auto location_obj = std::make_unique<sarif_location> (); - auto message_obj = builder.make_message_object_for_diagram (diagram); - location_obj->set<sarif_message> ("message", std::move (message_obj)); - - add_related_location (std::move (location_obj), builder); -} - -/* class sarif_location : public sarif_object. */ - -/* Ensure this location has an "id" and return it. - Use LOC_MGR if an id needs to be allocated. - - See the "id" property (3.28.2). - - We use this to only assign ids to locations that are - referenced by another sarif object; others have no "id". */ - -long -sarif_location::lazily_add_id (sarif_location_manager &loc_mgr) -{ - long id = get_id (); - if (id != -1) - return id; - id = loc_mgr.allocate_location_id (); - set_integer ("id", id); - gcc_assert (id != -1); - return id; -} - -/* Get the id of this location, or -1 if it doesn't have one. */ - -long -sarif_location::get_id () const -{ - json::value *id = get ("id"); - if (!id) - return -1; - gcc_assert (id->get_kind () == json::JSON_INTEGER); - return static_cast <json::integer_number *> (id)->get (); -} - -// 3.34.3 kinds property -static const char * -get_string_for_location_relationship_kind (enum location_relationship_kind kind) -{ - switch (kind) - { - default: - gcc_unreachable (); - case location_relationship_kind::includes: - return "includes"; - case location_relationship_kind::is_included_by: - return "isIncludedBy"; - case location_relationship_kind::relevant: - return "relevant"; - } -} - -/* Lazily populate this location's "relationships" property (3.28.7) - with the relationship of KIND to TARGET, creating objects - as necessary. - Use LOC_MGR for any locations that need "id" values. */ - -void -sarif_location::lazily_add_relationship (sarif_location &target, - enum location_relationship_kind kind, - sarif_location_manager &loc_mgr) -{ - sarif_location_relationship &relationship_obj - = lazily_add_relationship_object (target, loc_mgr); - - relationship_obj.lazily_add_kind (kind); -} - -/* Lazily populate this location's "relationships" property (3.28.7) - with a location_relationship to TARGET, creating objects - as necessary. - Use LOC_MGR for any locations that need "id" values. */ - -sarif_location_relationship & -sarif_location::lazily_add_relationship_object (sarif_location &target, - sarif_location_manager &loc_mgr) -{ - /* See if THIS already has a locationRelationship referencing TARGET. */ - auto iter = m_relationships_map.find (&target); - if (iter != m_relationships_map.end ()) - { - /* We already have a locationRelationship from THIS to TARGET. */ - sarif_location_relationship *relationship = iter->second; - gcc_assert (relationship->get_target_id() == target.get_id ()); - return *relationship; - } - - // Ensure that THIS has a "relationships" property (3.28.7). - json::array &relationships_arr = lazily_add_relationships_array (); - - /* No existing locationRelationship from THIS to TARGET; make one, - record it, and add it to the "relationships" array. */ - auto relationship_obj - = std::make_unique<sarif_location_relationship> (target, loc_mgr); - sarif_location_relationship *relationship = relationship_obj.get (); - auto kv - = std::pair<sarif_location *, - sarif_location_relationship *> (&target, relationship); - m_relationships_map.insert (kv); - - relationships_arr.append (std::move (relationship_obj)); - - return *relationship; -} - -/* Ensure this location has a "relationships" array (3.28.7). */ - -json::array & -sarif_location::lazily_add_relationships_array () -{ - const char *const property_name = "relationships"; - if (json::value *relationships = get (property_name)) - { - gcc_assert (relationships->get_kind () == json::JSON_ARRAY); - return *static_cast <json::array *> (relationships); - } - json::array *relationships_arr = new json::array (); - set (property_name, relationships_arr); - return *relationships_arr; -} - -/* class sarif_ice_notification : public sarif_location_manager. */ - -/* sarif_ice_notification's ctor. - DIAGNOSTIC is an internal compiler error. */ - -sarif_ice_notification:: -sarif_ice_notification (const diagnostic_info &diagnostic, - sarif_builder &builder, - std::unique_ptr<json::object> backtrace) -{ - /* "locations" property (SARIF v2.1.0 section 3.58.4). */ - auto locations_arr - = builder.make_locations_arr (*this, - diagnostic, - diagnostic_artifact_role::result_file); - set<json::array> ("locations", std::move (locations_arr)); - - /* "message" property (SARIF v2.1.0 section 3.85.5). */ - auto message_obj - = builder.make_message_object (pp_formatted_text (builder.get_printer ())); - pp_clear_output_area (builder.get_printer ()); - set<sarif_message> ("message", std::move (message_obj)); - - /* "level" property (SARIF v2.1.0 section 3.58.6). */ - set_string ("level", "error"); - - /* If we have backtrace information, add it as part of a property bag. */ - if (backtrace) - { - sarif_property_bag &bag = get_or_create_properties (); - bag.set ("gcc/backtrace", std::move (backtrace)); - } -} - -/* Implementation of sarif_location_manager::add_related_location vfunc - for notifications. */ - -void -sarif_ice_notification:: -add_related_location (std::unique_ptr<sarif_location> location_obj, - sarif_builder &builder) -{ - /* Support for related locations within a notification was added - in SARIF 2.2; see https://github.com/oasis-tcs/sarif-spec/issues/540 */ - if (builder.get_version () >= sarif_version::v2_2_prerelease_2024_08_08) - sarif_location_manager::add_related_location (std::move (location_obj), - builder); - /* Otherwise implicitly discard LOCATION_OBJ. */ -} - -/* class sarif_location_relationship : public sarif_object. */ - -sarif_location_relationship:: -sarif_location_relationship (sarif_location &target, - sarif_location_manager &loc_mgr) -: m_kinds ((unsigned)location_relationship_kind::NUM_KINDS) -{ - bitmap_clear (m_kinds); - set_integer ("target", target.lazily_add_id (loc_mgr)); -} - -long -sarif_location_relationship::get_target_id () const -{ - json::value *id = get ("id"); - gcc_assert (id); - return static_cast <json::integer_number *> (id)->get (); -} - -void -sarif_location_relationship:: -lazily_add_kind (enum location_relationship_kind kind) -{ - if (bitmap_bit_p (m_kinds, (int)kind)) - return; // already have this kind - bitmap_set_bit (m_kinds, (int)kind); - - // 3.34.3 kinds property - json::array *kinds_arr = nullptr; - if (json::value *kinds_val = get ("kinds")) - { - gcc_assert (kinds_val->get_kind () == json::JSON_ARRAY); - } - else - { - kinds_arr = new json::array (); - set ("kinds", kinds_arr); - } - const char *kind_str = get_string_for_location_relationship_kind (kind); - kinds_arr->append_string (kind_str); -} - -/* class sarif_code_flow : public sarif_object. */ - -sarif_code_flow::sarif_code_flow (sarif_result &parent, - unsigned idx_within_parent) -: m_parent (parent), - m_idx_within_parent (idx_within_parent) -{ - /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */ - auto thread_flows_arr = std::make_unique<json::array> (); - m_thread_flows_arr = thread_flows_arr.get (); // borrowed - set<json::array> ("threadFlows", std::move (thread_flows_arr)); -} - -sarif_thread_flow & -sarif_code_flow::get_or_append_thread_flow (const diagnostic_thread &thread, - diagnostic_thread_id_t thread_id) -{ - sarif_thread_flow **slot = m_thread_id_map.get (thread_id); - if (slot) - return **slot; - - unsigned next_thread_flow_idx = m_thread_flows_arr->size (); - auto thread_flow_obj - = std::make_unique<sarif_thread_flow> (*this, thread, next_thread_flow_idx); - m_thread_id_map.put (thread_id, thread_flow_obj.get ()); // borrowed - sarif_thread_flow *result = thread_flow_obj.get (); - m_thread_flows_arr->append<sarif_thread_flow> (std::move (thread_flow_obj)); - return *result; -} - -sarif_thread_flow & -sarif_code_flow::get_thread_flow (diagnostic_thread_id_t thread_id) -{ - sarif_thread_flow **slot = m_thread_id_map.get (thread_id); - gcc_assert (slot); // it must already have one - return **slot; -} - -void -sarif_code_flow::add_location (sarif_thread_flow_location &tfl_obj) -{ - m_all_tfl_objs.push_back (&tfl_obj); -} - -sarif_thread_flow_location & -sarif_code_flow::get_thread_flow_loc_obj (diagnostic_event_id_t event_id) const -{ - gcc_assert (event_id.known_p ()); - gcc_assert ((size_t)event_id.zero_based () < m_all_tfl_objs.size ()); - sarif_thread_flow_location *tfl_obj = m_all_tfl_objs[event_id.zero_based ()]; - gcc_assert (tfl_obj); - return *tfl_obj; -} - -/* class sarif_thread_flow : public sarif_object. */ - -sarif_thread_flow::sarif_thread_flow (sarif_code_flow &parent, - const diagnostic_thread &thread, - unsigned idx_within_parent) -: m_parent (parent), - m_idx_within_parent (idx_within_parent) -{ - /* "id" property (SARIF v2.1.0 section 3.37.2). */ - label_text name (thread.get_name (false)); - set_string ("id", name.get ()); - - /* "locations" property (SARIF v2.1.0 section 3.37.6). */ - m_locations_arr = new json::array (); - - /* Give ownership of m_locations_arr to json::object; - keep a borrowed ptr. */ - set ("locations", m_locations_arr); -} - -/* Add a sarif_thread_flow_location to this threadFlow object, but - don't populate it yet. */ - -sarif_thread_flow_location & -sarif_thread_flow::add_location () -{ - const unsigned thread_flow_location_idx = m_locations_arr->size (); - sarif_thread_flow_location *thread_flow_loc_obj - = new sarif_thread_flow_location (*this, thread_flow_location_idx); - m_locations_arr->append (thread_flow_loc_obj); - m_parent.add_location (*thread_flow_loc_obj); - return *thread_flow_loc_obj; -} - -/* class sarif_builder. */ - -/* sarif_builder's ctor. */ - -sarif_builder::sarif_builder (diagnostic_context &context, - pretty_printer &printer, - const line_maps *line_maps, - const char *main_input_filename_, - std::unique_ptr<sarif_serialization_format> serialization_format, - const sarif_generation_options &sarif_gen_opts) -: m_context (context), - m_printer (&printer), - m_line_maps (line_maps), - m_token_printer (*this), - m_logical_loc_mgr (nullptr), - m_invocation_obj - (std::make_unique<sarif_invocation> (*this, - context.get_original_argv ())), - m_results_array (new json::array ()), - m_cur_group_result (nullptr), - m_seen_any_relative_paths (false), - m_rule_id_set (), - m_rules_arr (new json::array ()), - m_cached_logical_locs - (std::make_unique<sarif_array_of_unique<sarif_logical_location>> ()), - m_tabstop (context.m_tabstop), - m_serialization_format (std::move (serialization_format)), - m_sarif_gen_opts (sarif_gen_opts), - m_next_result_idx (0), - m_current_code_flow (nullptr) -{ - gcc_assert (m_line_maps); - gcc_assert (m_serialization_format); - - if (auto client_data_hooks = context.get_client_data_hooks ()) - m_logical_loc_mgr = client_data_hooks->get_logical_location_manager (); - - /* Mark MAIN_INPUT_FILENAME_ as the artifact that the tool was - instructed to scan. - Only quote the contents if it gets referenced by physical locations, - since otherwise the "no diagnostics" case would quote the main input - file, and doing so noticeably bloated the output seen in analyzer - integration testing (build directory went from 20G -> 21G). */ - if (main_input_filename_) - get_or_create_artifact (main_input_filename_, - diagnostic_artifact_role::analysis_target, - false); -} - -sarif_builder::~sarif_builder () -{ - /* Normally m_filename_to_artifact_map will have been emptied as part - of make_run_object, but this isn't run by all the selftests. - Ensure the artifact objects are cleaned up for such cases. */ - for (auto iter : m_filename_to_artifact_map) - { - sarif_artifact *artifact_obj = iter.second; - delete artifact_obj; - } -} - -/* Functions at which to stop the backtrace print. It's not - particularly helpful to print the callers of these functions. */ - -static const char * const bt_stop[] = -{ - "main", - "toplev::main", - "execute_one_pass", - "compile_file", -}; - -struct bt_closure -{ - bt_closure (sarif_builder &builder, - json::array *frames_arr) - : m_builder (builder), - m_frames_arr (frames_arr) - { - } - - sarif_builder &m_builder; - json::array *m_frames_arr; -}; - -/* A callback function passed to the backtrace_full function. */ - -static int -bt_callback (void *data, uintptr_t pc, const char *filename, int lineno, - const char *function) -{ - bt_closure *closure = (bt_closure *)data; - - /* If we don't have any useful information, don't print - anything. */ - if (filename == NULL && function == NULL) - return 0; - - /* Skip functions in diagnostic.cc or diagnostic-global-context.cc. */ - if (closure->m_frames_arr->size () == 0 - && filename != NULL - && (strcmp (lbasename (filename), "diagnostic.cc") == 0 - || strcmp (lbasename (filename), - "diagnostic-global-context.cc") == 0)) - return 0; - - /* Print up to 20 functions. We could make this a --param, but - since this is only for debugging just use a constant for now. */ - if (closure->m_frames_arr->size () >= 20) - { - /* Returning a non-zero value stops the backtrace. */ - return 1; - } - - char *alc = NULL; - if (function != NULL) - { - char *str = cplus_demangle_v3 (function, - (DMGL_VERBOSE | DMGL_ANSI - | DMGL_GNU_V3 | DMGL_PARAMS)); - if (str != NULL) - { - alc = str; - function = str; - } - - for (size_t i = 0; i < ARRAY_SIZE (bt_stop); ++i) - { - size_t len = strlen (bt_stop[i]); - if (strncmp (function, bt_stop[i], len) == 0 - && (function[len] == '\0' || function[len] == '(')) - { - if (alc != NULL) - free (alc); - /* Returning a non-zero value stops the backtrace. */ - return 1; - } - } - } - - auto frame_obj = std::make_unique<json::object> (); - - /* I tried using sarifStack and sarifStackFrame for this - but it's not a good fit e.g. PC information. */ - char buf[128]; - snprintf (buf, sizeof (buf) - 1, "0x%lx", (unsigned long)pc); - frame_obj->set_string ("pc", buf); - if (function) - frame_obj->set_string ("function", function); - if (filename) - frame_obj->set_string ("filename", filename); - frame_obj->set_integer ("lineno", lineno); - closure->m_frames_arr->append (std::move (frame_obj)); - - if (alc != NULL) - free (alc); - - return 0; -} - -/* Attempt to generate a JSON object representing a backtrace, - for adding to ICE notifications. */ - -std::unique_ptr<json::object> -sarif_builder::make_stack_from_backtrace () -{ - auto frames_arr = std::make_unique<json::array> (); - - backtrace_state *state = nullptr; - state = backtrace_create_state (nullptr, 0, nullptr, nullptr); - bt_closure closure (*this, frames_arr.get ()); - const int frames_to_skip = 5; - if (state != nullptr) - backtrace_full (state, frames_to_skip, bt_callback, nullptr, - (void *) &closure); - - if (frames_arr->size () == 0) - return nullptr; - - auto stack = std::make_unique<json::object> (); - stack->set ("frames", std::move (frames_arr)); - return stack; -} - -/* Implementation of "on_report_diagnostic" for SARIF output. */ - -void -sarif_builder::on_report_diagnostic (const diagnostic_info &diagnostic, - diagnostic_t orig_diag_kind, - diagnostic_sarif_format_buffer *buffer) -{ - pp_output_formatted_text (m_printer, m_context.get_urlifier ()); - - if (diagnostic.kind == DK_ICE || diagnostic.kind == DK_ICE_NOBT) - { - std::unique_ptr<json::object> stack = make_stack_from_backtrace (); - m_invocation_obj->add_notification_for_ice (diagnostic, *this, - std::move (stack)); - - /* Print a header for the remaining output to stderr, and - return, attempting to print the usual ICE messages to - stderr. Hopefully this will be helpful to the user in - indicating what's gone wrong (also for DejaGnu, for pruning - those messages). */ - fnotice (stderr, "Internal compiler error:\n"); - - return; - } - - if (buffer) - { - /* When buffering, we can only handle top-level results. */ - gcc_assert (!m_cur_group_result); - buffer->add_result (make_result_object (diagnostic, orig_diag_kind, - m_next_result_idx++)); - return; - } - - if (m_cur_group_result) - /* Nested diagnostic. */ - m_cur_group_result->on_nested_diagnostic (diagnostic, - orig_diag_kind, - *this); - else - { - /* Top-level diagnostic. */ - m_cur_group_result = make_result_object (diagnostic, orig_diag_kind, - m_next_result_idx++); - } -} - -/* Implementation of diagnostic_context::m_diagrams.m_emission_cb - for SARIF output. */ - -void -sarif_builder::emit_diagram (const diagnostic_diagram &diagram) -{ - /* We must be within the emission of a top-level diagnostic. */ - gcc_assert (m_cur_group_result); - m_cur_group_result->on_diagram (diagram, *this); -} - -/* Implementation of "end_group_cb" for SARIF output. */ - -void -sarif_builder::end_group () -{ - if (m_cur_group_result) - { - m_cur_group_result->process_worklist (*this); - m_results_array->append<sarif_result> (std::move (m_cur_group_result)); - } -} - -/* Create a top-level object, and add it to all the results - (and other entities) we've seen so far, moving ownership - to the object. */ - -std::unique_ptr<sarif_log> -sarif_builder::flush_to_object () -{ - m_invocation_obj->prepare_to_flush (*this); - std::unique_ptr<sarif_log> top - = make_top_level_object (std::move (m_invocation_obj), - std::move (m_results_array)); - return top; -} - -/* Create a top-level object, and add it to all the results - (and other entities) we've seen so far. - - Flush it all to OUTF. */ - -void -sarif_builder::flush_to_file (FILE *outf) -{ - std::unique_ptr<sarif_log> top = flush_to_object (); - m_serialization_format->write_to_file (outf, *top); -} - -/* Attempt to convert DIAG_KIND to a suitable value for the "level" - property (SARIF v2.1.0 section 3.27.10). - - Return nullptr if there isn't one. */ - -static const char * -maybe_get_sarif_level (diagnostic_t diag_kind) -{ - switch (diag_kind) - { - case DK_WARNING: - return "warning"; - case DK_ERROR: - return "error"; - case DK_NOTE: - case DK_ANACHRONISM: - return "note"; - default: - return nullptr; - } -} - -/* Make a string for DIAG_KIND suitable for use a ruleId - (SARIF v2.1.0 section 3.27.5) as a fallback for when we don't - have anything better to use. */ - -static char * -make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind) -{ - /* Lose the trailing ": ". */ - const char *kind_text = get_diagnostic_kind_text (diag_kind); - size_t len = strlen (kind_text); - gcc_assert (len > 2); - gcc_assert (kind_text[len - 2] == ':'); - gcc_assert (kind_text[len - 1] == ' '); - char *rstrip = xstrdup (kind_text); - rstrip[len - 2] = '\0'; - return rstrip; -} - -/* Make a "result" object (SARIF v2.1.0 section 3.27) for DIAGNOSTIC. */ - -std::unique_ptr<sarif_result> -sarif_builder::make_result_object (const diagnostic_info &diagnostic, - diagnostic_t orig_diag_kind, - unsigned idx_within_parent) -{ - auto result_obj = std::make_unique<sarif_result> (idx_within_parent); - - /* "ruleId" property (SARIF v2.1.0 section 3.27.5). */ - /* Ideally we'd have an option_name for these. */ - if (char *option_text - = m_context.make_option_name (diagnostic.option_id, - orig_diag_kind, diagnostic.kind)) - { - /* Lazily create reportingDescriptor objects for and add to m_rules_arr. - Set ruleId referencing them. */ - result_obj->set_string ("ruleId", option_text); - if (m_rule_id_set.contains (option_text)) - free (option_text); - else - { - /* This is the first time we've seen this ruleId. */ - /* Add to set, taking ownership. */ - m_rule_id_set.add (option_text); - - m_rules_arr->append<sarif_reporting_descriptor> - (make_reporting_descriptor_object_for_warning (diagnostic, - orig_diag_kind, - option_text)); - } - } - else - { - /* Otherwise, we have an "error" or a stray "note"; use the - diagnostic kind as the ruleId, so that the result object at least - has a ruleId. - We don't bother creating reportingDescriptor objects for these. */ - char *rule_id = make_rule_id_for_diagnostic_kind (orig_diag_kind); - result_obj->set_string ("ruleId", rule_id); - free (rule_id); - } - - if (diagnostic.metadata) - { - /* "taxa" property (SARIF v2.1.0 section 3.27.8). */ - if (int cwe_id = diagnostic.metadata->get_cwe ()) - { - auto taxa_arr = std::make_unique<json::array> (); - taxa_arr->append<sarif_reporting_descriptor_reference> - (make_reporting_descriptor_reference_object_for_cwe_id (cwe_id)); - result_obj->set<json::array> ("taxa", std::move (taxa_arr)); - } - - diagnostic.metadata->maybe_add_sarif_properties (*result_obj); - - /* We don't yet support diagnostic_metadata::rule. */ - } - - /* "level" property (SARIF v2.1.0 section 3.27.10). */ - if (const char *sarif_level = maybe_get_sarif_level (diagnostic.kind)) - result_obj->set_string ("level", sarif_level); - - /* "message" property (SARIF v2.1.0 section 3.27.11). */ - auto message_obj - = make_message_object (pp_formatted_text (m_printer)); - pp_clear_output_area (m_printer); - result_obj->set<sarif_message> ("message", std::move (message_obj)); - - /* "locations" property (SARIF v2.1.0 section 3.27.12). */ - result_obj->set<json::array> - ("locations", - make_locations_arr (*result_obj.get (), - diagnostic, - diagnostic_artifact_role::result_file)); - - /* "codeFlows" property (SARIF v2.1.0 section 3.27.18). */ - if (const diagnostic_path *path = diagnostic.richloc->get_path ()) - { - auto code_flows_arr = std::make_unique<json::array> (); - const unsigned code_flow_index = 0; - code_flows_arr->append<sarif_code_flow> - (make_code_flow_object (*result_obj.get (), - code_flow_index, - *path)); - result_obj->set<json::array> ("codeFlows", std::move (code_flows_arr)); - } - - /* The "relatedLocations" property (SARIF v2.1.0 section 3.27.22) is - set up later, if any nested diagnostics occur within this diagnostic - group. */ - - /* "fixes" property (SARIF v2.1.0 section 3.27.30). */ - const rich_location *richloc = diagnostic.richloc; - if (richloc->get_num_fixit_hints ()) - { - auto fix_arr = std::make_unique<json::array> (); - fix_arr->append<sarif_fix> (make_fix_object (*richloc)); - result_obj->set<json::array> ("fixes", std::move (fix_arr)); - } - - return result_obj; -} - -/* Make a "reportingDescriptor" object (SARIF v2.1.0 section 3.49) - for a GCC warning. */ - -std::unique_ptr<sarif_reporting_descriptor> -sarif_builder:: -make_reporting_descriptor_object_for_warning (const diagnostic_info &diagnostic, - diagnostic_t /*orig_diag_kind*/, - const char *option_text) -{ - auto reporting_desc = std::make_unique<sarif_reporting_descriptor> (); - - /* "id" property (SARIF v2.1.0 section 3.49.3). */ - reporting_desc->set_string ("id", option_text); - - /* We don't implement "name" property (SARIF v2.1.0 section 3.49.7), since - it seems redundant compared to "id". */ - - /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */ - if (char *option_url = m_context.make_option_url (diagnostic.option_id)) - { - reporting_desc->set_string ("helpUri", option_url); - free (option_url); - } - - return reporting_desc; -} - -/* Make a "reportingDescriptor" object (SARIF v2.1.0 section 3.49) - for CWE_ID, for use within the CWE taxa array. */ - -std::unique_ptr<sarif_reporting_descriptor> -sarif_builder::make_reporting_descriptor_object_for_cwe_id (int cwe_id) const -{ - auto reporting_desc = std::make_unique<sarif_reporting_descriptor> (); - - /* "id" property (SARIF v2.1.0 section 3.49.3). */ - { - pretty_printer pp; - pp_printf (&pp, "%i", cwe_id); - reporting_desc->set_string ("id", pp_formatted_text (&pp)); - } - - /* "helpUri" property (SARIF v2.1.0 section 3.49.12). */ - { - char *url = get_cwe_url (cwe_id); - reporting_desc->set_string ("helpUri", url); - free (url); - } - - return reporting_desc; -} - -/* Make a "reportingDescriptorReference" object (SARIF v2.1.0 section 3.52) - referencing CWE_ID, for use within a result object. - Also, add CWE_ID to m_cwe_id_set. */ - -std::unique_ptr<sarif_reporting_descriptor_reference> -sarif_builder:: -make_reporting_descriptor_reference_object_for_cwe_id (int cwe_id) -{ - auto desc_ref_obj = std::make_unique<sarif_reporting_descriptor_reference> (); - - /* "id" property (SARIF v2.1.0 section 3.52.4). */ - { - pretty_printer pp; - pp_printf (&pp, "%i", cwe_id); - desc_ref_obj->set_string ("id", pp_formatted_text (&pp)); - } - - /* "toolComponent" property (SARIF v2.1.0 section 3.52.7). */ - desc_ref_obj->set<sarif_tool_component_reference> - ("toolComponent", make_tool_component_reference_object_for_cwe ()); - - /* Add CWE_ID to our set. */ - gcc_assert (cwe_id > 0); - m_cwe_id_set.add (cwe_id); - - return desc_ref_obj; -} - -/* Make a "toolComponentReference" object (SARIF v2.1.0 section 3.54) that - references the CWE taxonomy. */ - -std::unique_ptr<sarif_tool_component_reference> -sarif_builder:: -make_tool_component_reference_object_for_cwe () const -{ - auto comp_ref_obj = std::make_unique<sarif_tool_component_reference> (); - - /* "name" property (SARIF v2.1.0 section 3.54.3). */ - comp_ref_obj->set_string ("name", "cwe"); - - return comp_ref_obj; -} - -/* Make an array suitable for use as the "locations" property of: - - a "result" object (SARIF v2.1.0 section 3.27.12), or - - a "notification" object (SARIF v2.1.0 section 3.58.4). - Use LOC_MGR for any locations that need "id" values. */ - -std::unique_ptr<json::array> -sarif_builder::make_locations_arr (sarif_location_manager &loc_mgr, - const diagnostic_info &diagnostic, - enum diagnostic_artifact_role role) -{ - auto locations_arr = std::make_unique<json::array> (); - logical_location logical_loc; - if (auto client_data_hooks = m_context.get_client_data_hooks ()) - logical_loc = client_data_hooks->get_current_logical_location (); - - auto location_obj - = make_location_object (loc_mgr, *diagnostic.richloc, logical_loc, role); - /* Don't add entirely empty location objects to the array. */ - if (!location_obj->is_empty ()) - locations_arr->append<sarif_location> (std::move (location_obj)); - - return locations_arr; -} - -/* If LOGICAL_LOC is non-null, use it to create a "logicalLocations" property - within LOCATION_OBJ (SARIF v2.1.0 section 3.28.4) with a minimal logical - location object referencing theRuns.logicalLocations (3.33.3). */ - -void -sarif_builder:: -set_any_logical_locs_arr (sarif_location &location_obj, - logical_location logical_loc) -{ - if (!logical_loc) - return; - gcc_assert (m_logical_loc_mgr); - auto location_locs_arr = std::make_unique<json::array> (); - - auto logical_loc_obj = make_minimal_sarif_logical_location (logical_loc); - - location_locs_arr->append<sarif_logical_location> - (std::move (logical_loc_obj)); - - location_obj.set<json::array> ("logicalLocations", - std::move (location_locs_arr)); -} - -/* Make a "location" object (SARIF v2.1.0 section 3.28) for RICH_LOC - and LOGICAL_LOC. - Use LOC_MGR for any locations that need "id" values, and for - any worklist items. */ - -std::unique_ptr<sarif_location> -sarif_builder::make_location_object (sarif_location_manager &loc_mgr, - const rich_location &rich_loc, - logical_location logical_loc, - enum diagnostic_artifact_role role) -{ - class escape_nonascii_renderer : public content_renderer - { - public: - escape_nonascii_renderer (const rich_location &richloc, - enum diagnostics_escape_format escape_format) - : m_richloc (richloc), - m_escape_format (escape_format) - {} - - std::unique_ptr<sarif_multiformat_message_string> - render (const sarif_builder &builder) const final override - { - diagnostic_context dc; - diagnostic_initialize (&dc, 0); - dc.m_source_printing.enabled = true; - dc.m_source_printing.colorize_source_p = false; - dc.m_source_printing.show_labels_p = true; - dc.m_source_printing.show_line_numbers_p = true; - - rich_location my_rich_loc (m_richloc); - my_rich_loc.set_escape_on_output (true); - - diagnostic_source_print_policy source_policy (dc); - dc.set_escape_format (m_escape_format); - diagnostic_text_output_format text_output (dc); - source_policy.print (*text_output.get_printer (), - my_rich_loc, DK_ERROR, nullptr); - - const char *buf = pp_formatted_text (text_output.get_printer ()); - std::unique_ptr<sarif_multiformat_message_string> result - = builder.make_multiformat_message_string (buf); - - diagnostic_finish (&dc); - - return result; - } - private: - const rich_location &m_richloc; - enum diagnostics_escape_format m_escape_format; - } the_renderer (rich_loc, - m_context.get_escape_format ()); - - auto location_obj = std::make_unique<sarif_location> (); - - /* Get primary loc from RICH_LOC. */ - location_t loc = rich_loc.get_loc (); - - /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */ - const content_renderer *snippet_renderer - = rich_loc.escape_on_output_p () ? &the_renderer : nullptr; - if (auto phs_loc_obj - = maybe_make_physical_location_object (loc, role, - rich_loc.get_column_override (), - snippet_renderer)) - location_obj->set<sarif_physical_location> ("physicalLocation", - std::move (phs_loc_obj)); - - /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */ - set_any_logical_locs_arr (*location_obj, logical_loc); - - /* Handle labelled ranges and/or secondary locations. */ - { - std::unique_ptr<json::array> annotations_arr = nullptr; - for (unsigned int i = 0; i < rich_loc.get_num_locations (); i++) - { - const location_range *range = rich_loc.get_range (i); - bool handled = false; - if (const range_label *label = range->m_label) - { - label_text text = label->get_text (i); - if (text.get ()) - { - /* Create annotations for any labelled ranges. */ - location_t range_loc = rich_loc.get_loc (i); - auto region - = maybe_make_region_object (range_loc, - rich_loc.get_column_override ()); - if (region) - { - if (!annotations_arr) - annotations_arr = std::make_unique<json::array> (); - region->set<sarif_message> - ("message", make_message_object (text.get ())); - annotations_arr->append<sarif_region> (std::move (region)); - handled = true; - } - } - } - - /* Add related locations for any secondary locations in RICH_LOC - that don't have labels (and thus aren't added to "annotations"). */ - if (i > 0 && !handled) - loc_mgr.add_relationship_to_worklist - (*location_obj.get (), - sarif_location_manager::worklist_item::kind::unlabelled_secondary_location, - range->m_loc); - } - if (annotations_arr) - /* "annotations" property (SARIF v2.1.0 section 3.28.6). */ - location_obj->set<json::array> ("annotations", - std::move (annotations_arr)); - } - - add_any_include_chain (loc_mgr, *location_obj.get (), loc); - - /* A flag for hinting that the diagnostic involves issues at the - level of character encodings (such as homoglyphs, or misleading - bidirectional control codes), and thus that it will be helpful - to the user if we show some representation of - how the characters in the pertinent source lines are encoded. */ - if (rich_loc.escape_on_output_p ()) - { - sarif_property_bag &bag = location_obj->get_or_create_properties (); - bag.set_bool ("gcc/escapeNonAscii", rich_loc.escape_on_output_p ()); - } - - return location_obj; -} - -/* If WHERE was #included from somewhere, add a worklist item - to LOC_MGR to lazily add a location for the #include location, - and relationships between it and the LOCATION_OBJ. - Compare with diagnostic_context::report_current_module, but rather - than iterating the current chain, we add the next edge and iterate - in the worklist, so that edges are only added once. */ - -void -sarif_builder::add_any_include_chain (sarif_location_manager &loc_mgr, - sarif_location &location_obj, - location_t where) -{ - if (where <= BUILTINS_LOCATION) - return; - - const line_map_ordinary *map = nullptr; - linemap_resolve_location (m_line_maps, where, - LRK_MACRO_DEFINITION_LOCATION, - &map); - - if (!map) - return; - - location_t include_loc = linemap_included_from (map); - map = linemap_included_from_linemap (m_line_maps, map); - if (!map) - return; - loc_mgr.add_relationship_to_worklist - (location_obj, - sarif_result::worklist_item::kind::included_from, - include_loc); -} - -/* Make a "location" object (SARIF v2.1.0 section 3.28) for WHERE - within an include chain. */ - -std::unique_ptr<sarif_location> -sarif_builder::make_location_object (sarif_location_manager &loc_mgr, - location_t loc, - enum diagnostic_artifact_role role) -{ - auto location_obj = std::make_unique<sarif_location> (); - - /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */ - if (auto phs_loc_obj - = maybe_make_physical_location_object (loc, role, 0, nullptr)) - location_obj->set<sarif_physical_location> ("physicalLocation", - std::move (phs_loc_obj)); - - add_any_include_chain (loc_mgr, *location_obj.get (), loc); - - return location_obj; -} - -/* Make a "location" object (SARIF v2.1.0 section 3.28) for EVENT - within a diagnostic_path. */ - -std::unique_ptr<sarif_location> -sarif_builder::make_location_object (sarif_location_manager &loc_mgr, - const diagnostic_event &event, - enum diagnostic_artifact_role role) -{ - auto location_obj = std::make_unique<sarif_location> (); - - /* "physicalLocation" property (SARIF v2.1.0 section 3.28.3). */ - location_t loc = event.get_location (); - if (auto phs_loc_obj - = maybe_make_physical_location_object (loc, role, 0, nullptr)) - location_obj->set<sarif_physical_location> ("physicalLocation", - std::move (phs_loc_obj)); - - /* "logicalLocations" property (SARIF v2.1.0 section 3.28.4). */ - logical_location logical_loc = event.get_logical_location (); - set_any_logical_locs_arr (*location_obj, logical_loc); - - /* "message" property (SARIF v2.1.0 section 3.28.5). */ - std::unique_ptr<pretty_printer> pp = get_printer ()->clone (); - event.print_desc (*pp); - location_obj->set<sarif_message> - ("message", - make_message_object (pp_formatted_text (pp.get ()))); - - add_any_include_chain (loc_mgr, *location_obj.get (), loc); - - return location_obj; -} - -/* Make a "physicalLocation" object (SARIF v2.1.0 section 3.29) for LOC. - - If COLUMN_OVERRIDE is non-zero, then use it as the column number - if LOC has no column information. - - Ensure that we have an artifact object for the file, adding ROLE to it, - and flagging that we will attempt to embed the contents of the artifact - when writing it out. */ - -std::unique_ptr<sarif_physical_location> -sarif_builder:: -maybe_make_physical_location_object (location_t loc, - enum diagnostic_artifact_role role, - int column_override, - const content_renderer *snippet_renderer) -{ - if (loc <= BUILTINS_LOCATION || LOCATION_FILE (loc) == nullptr) - return nullptr; - - auto phys_loc_obj = std::make_unique<sarif_physical_location> (); - - /* "artifactLocation" property (SARIF v2.1.0 section 3.29.3). */ - phys_loc_obj->set<sarif_artifact_location> - ("artifactLocation", make_artifact_location_object (loc)); - get_or_create_artifact (LOCATION_FILE (loc), role, true); - - /* "region" property (SARIF v2.1.0 section 3.29.4). */ - if (auto region_obj = maybe_make_region_object (loc, column_override)) - phys_loc_obj->set<sarif_region> ("region", std::move (region_obj)); - - /* "contextRegion" property (SARIF v2.1.0 section 3.29.5). */ - if (auto context_region_obj - = maybe_make_region_object_for_context (loc, snippet_renderer)) - phys_loc_obj->set<sarif_region> ("contextRegion", - std::move (context_region_obj)); - - /* Instead, we add artifacts to the run as a whole, - with artifact.contents. - Could do both, though. */ - - return phys_loc_obj; -} - -/* Make an "artifactLocation" object (SARIF v2.1.0 section 3.4) for LOC, - or return nullptr. */ - -std::unique_ptr<sarif_artifact_location> -sarif_builder::make_artifact_location_object (location_t loc) -{ - return make_artifact_location_object (LOCATION_FILE (loc)); -} - -/* The ID value for use in "uriBaseId" properties (SARIF v2.1.0 section 3.4.4) - for when we need to express paths relative to PWD. */ - -#define PWD_PROPERTY_NAME ("PWD") - -/* Make an "artifactLocation" object (SARIF v2.1.0 section 3.4) for FILENAME, - or return nullptr. */ - -std::unique_ptr<sarif_artifact_location> -sarif_builder::make_artifact_location_object (const char *filename) -{ - auto artifact_loc_obj = std::make_unique<sarif_artifact_location> (); - - /* "uri" property (SARIF v2.1.0 section 3.4.3). */ - artifact_loc_obj->set_string ("uri", filename); - - if (filename[0] != '/') - { - /* If we have a relative path, set the "uriBaseId" property - (SARIF v2.1.0 section 3.4.4). */ - artifact_loc_obj->set_string ("uriBaseId", PWD_PROPERTY_NAME); - m_seen_any_relative_paths = true; - } - - return artifact_loc_obj; -} - -/* Get the PWD, or nullptr, as an absolute file-based URI, - adding a trailing forward slash (as required by SARIF v2.1.0 - section 3.14.14). */ - -static char * -make_pwd_uri_str () -{ - /* The prefix of a file-based URI, up to, but not including the path. */ -#define FILE_PREFIX ("file://") - - const char *pwd = getpwd (); - if (!pwd) - return nullptr; - size_t len = strlen (pwd); - if (len == 0 || pwd[len - 1] != '/') - return concat (FILE_PREFIX, pwd, "/", nullptr); - else - { - gcc_assert (pwd[len - 1] == '/'); - return concat (FILE_PREFIX, pwd, nullptr); - } -} - -/* Make an "artifactLocation" object (SARIF v2.1.0 section 3.4) for the pwd, - for use in the "run.originalUriBaseIds" property (SARIF v2.1.0 - section 3.14.14) when we have any relative paths. */ - -std::unique_ptr<sarif_artifact_location> -sarif_builder::make_artifact_location_object_for_pwd () const -{ - auto artifact_loc_obj = std::make_unique<sarif_artifact_location> (); - - /* "uri" property (SARIF v2.1.0 section 3.4.3). */ - if (char *pwd = make_pwd_uri_str ()) - { - gcc_assert (strlen (pwd) > 0); - gcc_assert (pwd[strlen (pwd) - 1] == '/'); - artifact_loc_obj->set_string ("uri", pwd); - free (pwd); - } - - return artifact_loc_obj; -} - -/* Get the column number within EXPLOC. */ - -int -sarif_builder::get_sarif_column (expanded_location exploc) const -{ - cpp_char_column_policy policy (m_tabstop, cpp_wcwidth); - return location_compute_display_column (m_context.get_file_cache (), - exploc, policy); -} - -/* Make a "region" object (SARIF v2.1.0 section 3.30) for LOC, - or return nullptr. - - If COLUMN_OVERRIDE is non-zero, then use it as the column number - if LOC has no column information. - - We only support text properties of regions ("text regions"), - not binary properties ("binary regions"); see 3.30.1. */ - -std::unique_ptr<sarif_region> -sarif_builder::maybe_make_region_object (location_t loc, - int column_override) const -{ - location_t caret_loc = get_pure_location (loc); - - if (caret_loc <= BUILTINS_LOCATION) - return nullptr; - - location_t start_loc = get_start (loc); - location_t finish_loc = get_finish (loc); - - expanded_location exploc_caret = expand_location (caret_loc); - expanded_location exploc_start = expand_location (start_loc); - expanded_location exploc_finish = expand_location (finish_loc); - - if (exploc_start.file !=exploc_caret.file) - return nullptr; - if (exploc_finish.file !=exploc_caret.file) - return nullptr; - - /* We can have line == 0 in the presence of "#" lines. - SARIF requires lines > 0, so if we hit this case we don't have a - way of validly representing the region as SARIF; bail out. */ - if (exploc_start.line <= 0) - return nullptr; - - auto region_obj = std::make_unique<sarif_region> (); - - /* "startLine" property (SARIF v2.1.0 section 3.30.5) */ - region_obj->set_integer ("startLine", exploc_start.line); - - /* "startColumn" property (SARIF v2.1.0 section 3.30.6). - - We use column == 0 to mean the whole line, so omit the column - information for this case, unless COLUMN_OVERRIDE is non-zero, - (for handling certain awkward lexer diagnostics) */ - - if (exploc_start.column == 0 && column_override) - /* Use the provided column number. */ - exploc_start.column = column_override; - - if (exploc_start.column > 0) - { - int start_column = get_sarif_column (exploc_start); - region_obj->set_integer ("startColumn", start_column); - } - - /* "endLine" property (SARIF v2.1.0 section 3.30.7) */ - if (exploc_finish.line != exploc_start.line - && exploc_finish.line > 0) - region_obj->set_integer ("endLine", exploc_finish.line); - - /* "endColumn" property (SARIF v2.1.0 section 3.30.8). - This expresses the column immediately beyond the range. - - We use column == 0 to mean the whole line, so omit the column - information for this case. */ - if (exploc_finish.column > 0) - { - int next_column = get_sarif_column (exploc_finish) + 1; - region_obj->set_integer ("endColumn", next_column); - } - - return region_obj; -} - -/* Make a "region" object (SARIF v2.1.0 section 3.30) for the "contextRegion" - property (SARIF v2.1.0 section 3.29.5) of a "physicalLocation". - - This is similar to maybe_make_region_object, but ignores column numbers, - covering the line(s) as a whole, and including a "snippet" property - embedding those source lines, making it easier for consumers to show - the pertinent source. */ - -std::unique_ptr<sarif_region> -sarif_builder:: -maybe_make_region_object_for_context (location_t loc, - const content_renderer *snippet_renderer) - const -{ - location_t caret_loc = get_pure_location (loc); - - if (caret_loc <= BUILTINS_LOCATION) - return nullptr; - - location_t start_loc = get_start (loc); - location_t finish_loc = get_finish (loc); - - expanded_location exploc_caret = expand_location (caret_loc); - expanded_location exploc_start = expand_location (start_loc); - expanded_location exploc_finish = expand_location (finish_loc); - - if (exploc_start.file !=exploc_caret.file) - return nullptr; - if (exploc_finish.file !=exploc_caret.file) - return nullptr; - - /* We can have line == 0 in the presence of "#" lines. - SARIF requires lines > 0, so if we hit this case we don't have a - way of validly representing the region as SARIF; bail out. */ - if (exploc_start.line <= 0) - return nullptr; - - auto region_obj = std::make_unique<sarif_region> (); - - /* "startLine" property (SARIF v2.1.0 section 3.30.5) */ - region_obj->set_integer ("startLine", exploc_start.line); - - /* "endLine" property (SARIF v2.1.0 section 3.30.7) */ - if (exploc_finish.line != exploc_start.line - && exploc_finish.line > 0) - region_obj->set_integer ("endLine", exploc_finish.line); - - /* "snippet" property (SARIF v2.1.0 section 3.30.13). */ - if (auto artifact_content_obj - = maybe_make_artifact_content_object (exploc_start.file, - exploc_start.line, - exploc_finish.line, - snippet_renderer)) - region_obj->set<sarif_artifact_content> ("snippet", - std::move (artifact_content_obj)); - - return region_obj; -} - -/* Make a "region" object (SARIF v2.1.0 section 3.30) for the deletion region - of HINT (as per SARIF v2.1.0 section 3.57.3). */ - -std::unique_ptr<sarif_region> -sarif_builder::make_region_object_for_hint (const fixit_hint &hint) const -{ - location_t start_loc = hint.get_start_loc (); - location_t next_loc = hint.get_next_loc (); - - expanded_location exploc_start = expand_location (start_loc); - expanded_location exploc_next = expand_location (next_loc); - - auto region_obj = std::make_unique<sarif_region> (); - - /* "startLine" property (SARIF v2.1.0 section 3.30.5) */ - region_obj->set_integer ("startLine", exploc_start.line); - - /* "startColumn" property (SARIF v2.1.0 section 3.30.6) */ - int start_col = get_sarif_column (exploc_start); - region_obj->set_integer ("startColumn", start_col); - - /* "endLine" property (SARIF v2.1.0 section 3.30.7) */ - if (exploc_next.line != exploc_start.line) - region_obj->set_integer ("endLine", exploc_next.line); - - /* "endColumn" property (SARIF v2.1.0 section 3.30.8). - This expresses the column immediately beyond the range. */ - int next_col = get_sarif_column (exploc_next); - region_obj->set_integer ("endColumn", next_col); - - return region_obj; -} - -/* Attempt to get a string for a logicalLocation's "kind" property - (SARIF v2.1.0 section 3.33.7). - Return nullptr if unknown. */ - -static const char * -maybe_get_sarif_kind (enum logical_location_kind kind) -{ - switch (kind) - { - default: - gcc_unreachable (); - case LOGICAL_LOCATION_KIND_UNKNOWN: - return nullptr; - - /* Kinds within executable code. */ - case LOGICAL_LOCATION_KIND_FUNCTION: - return "function"; - case LOGICAL_LOCATION_KIND_MEMBER: - return "member"; - case LOGICAL_LOCATION_KIND_MODULE: - return "module"; - case LOGICAL_LOCATION_KIND_NAMESPACE: - return "namespace"; - case LOGICAL_LOCATION_KIND_TYPE: - return "type"; - case LOGICAL_LOCATION_KIND_RETURN_TYPE: - return "returnType"; - case LOGICAL_LOCATION_KIND_PARAMETER: - return "parameter"; - case LOGICAL_LOCATION_KIND_VARIABLE: - return "variable"; - - /* Kinds within XML or HTML documents. */ - case LOGICAL_LOCATION_KIND_ELEMENT: - return "element"; - case LOGICAL_LOCATION_KIND_ATTRIBUTE: - return "attribute"; - case LOGICAL_LOCATION_KIND_TEXT: - return "text"; - case LOGICAL_LOCATION_KIND_COMMENT: - return "comment"; - case LOGICAL_LOCATION_KIND_PROCESSING_INSTRUCTION: - return "processingInstruction"; - case LOGICAL_LOCATION_KIND_DTD: - return "dtd"; - case LOGICAL_LOCATION_KIND_DECLARATION: - return "declaration"; - - /* Kinds within JSON documents. */ - case LOGICAL_LOCATION_KIND_OBJECT: - return "object"; - case LOGICAL_LOCATION_KIND_ARRAY: - return "array"; - case LOGICAL_LOCATION_KIND_PROPERTY: - return "property"; - case LOGICAL_LOCATION_KIND_VALUE: - return "value"; - } -} - -/* Set PROPERTY_NAME within this bag to a "logicalLocation" object (SARIF v2.1.0 - section 3.33) for LOGICAL_LOC. The object has an "index" property to refer to - theRuns.logicalLocations (3.33.3). */ - -void -sarif_property_bag::set_logical_location (const char *property_name, - sarif_builder &builder, - logical_location logical_loc) -{ - set<sarif_logical_location> - (property_name, - builder.make_minimal_sarif_logical_location (logical_loc)); -} - -/* Ensure that m_cached_logical_locs has a "logicalLocation" object - (SARIF v2.1.0 section 3.33) for K, and return its index within the - array. */ - -int -sarif_builder:: -ensure_sarif_logical_location_for (logical_location k) -{ - gcc_assert (m_logical_loc_mgr); - - auto sarif_logical_loc = std::make_unique<sarif_logical_location> (); - - if (const char *short_name = m_logical_loc_mgr->get_short_name (k)) - sarif_logical_loc->set_string ("name", short_name); - - /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */ - if (const char *name_with_scope = m_logical_loc_mgr->get_name_with_scope (k)) - sarif_logical_loc->set_string ("fullyQualifiedName", name_with_scope); - - /* "decoratedName" property (SARIF v2.1.0 section 3.33.6). */ - if (const char *internal_name = m_logical_loc_mgr->get_internal_name (k)) - sarif_logical_loc->set_string ("decoratedName", internal_name); - - /* "kind" property (SARIF v2.1.0 section 3.33.7). */ - enum logical_location_kind kind = m_logical_loc_mgr->get_kind (k); - if (const char *sarif_kind_str = maybe_get_sarif_kind (kind)) - sarif_logical_loc->set_string ("kind", sarif_kind_str); - - /* "parentIndex" property (SARIF v2.1.0 section 3.33.8). */ - if (auto parent_key = m_logical_loc_mgr->get_parent (k)) - { - /* Recurse upwards. */ - int parent_index = ensure_sarif_logical_location_for (parent_key); - sarif_logical_loc->set_integer ("parentIndex", parent_index); - } - - /* Consolidate if this logical location already exists. */ - int index - = m_cached_logical_locs->append_uniquely (std::move (sarif_logical_loc)); - - return index; -} - -/* Ensure that theRuns.logicalLocations (3.14.17) has a "logicalLocation" object - (SARIF v2.1.0 section 3.33) for LOGICAL_LOC. - Create and return a minimal logicalLocation object referring to the - full object by index. */ - -std::unique_ptr<sarif_logical_location> -sarif_builder:: -make_minimal_sarif_logical_location (logical_location logical_loc) -{ - gcc_assert (m_logical_loc_mgr); - - /* Ensure that m_cached_logical_locs has a "logicalLocation" object - (SARIF v2.1.0 section 3.33) for LOGICAL_LOC, and return its index within - the array. */ - - auto sarif_logical_loc = std::make_unique <sarif_logical_location> (); - - int index = ensure_sarif_logical_location_for (logical_loc); - - // 3.33.3 index property - sarif_logical_loc->set_integer ("index", index); - - /* "fullyQualifiedName" property (SARIF v2.1.0 section 3.33.5). */ - if (const char *name_with_scope - = m_logical_loc_mgr->get_name_with_scope (logical_loc)) - sarif_logical_loc->set_string ("fullyQualifiedName", name_with_scope); - - return sarif_logical_loc; -} - -label_text -make_sarif_url_for_event (const sarif_code_flow *code_flow, - diagnostic_event_id_t event_id) -{ - gcc_assert (event_id.known_p ()); - - if (!code_flow) - return label_text (); - - const sarif_thread_flow_location &tfl_obj - = code_flow->get_thread_flow_loc_obj (event_id); - const int location_idx = tfl_obj.get_index_within_parent (); - - const sarif_thread_flow &thread_flow_obj = tfl_obj.get_parent (); - const int thread_flow_idx = thread_flow_obj.get_index_within_parent (); - - const sarif_code_flow &code_flow_obj = thread_flow_obj.get_parent (); - const int code_flow_idx = code_flow_obj.get_index_within_parent (); - - const sarif_result &result_obj = code_flow_obj.get_parent (); - const int result_idx = result_obj.get_index_within_parent (); - - /* We only support a single run object in the log. */ - const int run_idx = 0; - - char *buf = xasprintf - ("sarif:/runs/%i/results/%i/codeFlows/%i/threadFlows/%i/locations/%i", - run_idx, result_idx, code_flow_idx, thread_flow_idx, location_idx); - return label_text::take (buf); -} - -/* Make a "codeFlow" object (SARIF v2.1.0 section 3.36) for PATH. */ - -std::unique_ptr<sarif_code_flow> -sarif_builder::make_code_flow_object (sarif_result &result, - unsigned idx_within_parent, - const diagnostic_path &path) -{ - auto code_flow_obj - = std::make_unique <sarif_code_flow> (result, idx_within_parent); - - /* First pass: - Create threadFlows and threadFlowLocation objects within them, - effectively recording a mapping from event_id to threadFlowLocation - so that we can later go from an event_id to a URI within the - SARIF file. */ - for (unsigned i = 0; i < path.num_events (); i++) - { - const diagnostic_event &event = path.get_event (i); - const diagnostic_thread_id_t thread_id = event.get_thread_id (); - - sarif_thread_flow &thread_flow_obj - = code_flow_obj->get_or_append_thread_flow (path.get_thread (thread_id), - thread_id); - thread_flow_obj.add_location (); - } - - /* Second pass: walk the events, populating the tfl objs. */ - m_current_code_flow = code_flow_obj.get (); - for (unsigned i = 0; i < path.num_events (); i++) - { - const diagnostic_event &event = path.get_event (i); - sarif_thread_flow_location &thread_flow_loc_obj - = code_flow_obj->get_thread_flow_loc_obj (i); - populate_thread_flow_location_object (result, - thread_flow_loc_obj, - event, - i); - } - m_current_code_flow = nullptr; - - return code_flow_obj; -} - -/* Populate TFL_OBJ, a "threadFlowLocation" object (SARIF v2.1.0 section 3.38) - based on EVENT. */ - -void -sarif_builder:: -populate_thread_flow_location_object (sarif_result &result, - sarif_thread_flow_location &tfl_obj, - const diagnostic_event &ev, - int event_execution_idx) -{ - /* Give diagnostic_event subclasses a chance to add custom properties - via a property bag. */ - ev.maybe_add_sarif_properties (*this, tfl_obj); - - /* "location" property (SARIF v2.1.0 section 3.38.3). */ - tfl_obj.set<sarif_location> - ("location", - make_location_object (result, ev, diagnostic_artifact_role::traced_file)); - - /* "kinds" property (SARIF v2.1.0 section 3.38.8). */ - diagnostic_event::meaning m = ev.get_meaning (); - if (auto kinds_arr = maybe_make_kinds_array (m)) - tfl_obj.set<json::array> ("kinds", std::move (kinds_arr)); - - /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */ - tfl_obj.set_integer ("nestingLevel", ev.get_stack_depth ()); - - /* "executionOrder" property (SARIF v2.1.0 3.38.11). - Offset by 1 to match the human-readable values emitted by %@. */ - tfl_obj.set_integer ("executionOrder", event_execution_idx + 1); - - /* It might be nice to eventually implement the following for -fanalyzer: - - the "stack" property (SARIF v2.1.0 section 3.38.5) - - the "state" property (SARIF v2.1.0 section 3.38.9) - - the "importance" property (SARIF v2.1.0 section 3.38.13). */ -} - -/* If M has any known meaning, make a json array suitable for the "kinds" - property of a "threadFlowLocation" object (SARIF v2.1.0 section 3.38.8). - - Otherwise, return nullptr. */ - -std::unique_ptr<json::array> -sarif_builder::maybe_make_kinds_array (diagnostic_event::meaning m) const -{ - if (m.m_verb == diagnostic_event::VERB_unknown - && m.m_noun == diagnostic_event::NOUN_unknown - && m.m_property == diagnostic_event::PROPERTY_unknown) - return nullptr; - - auto kinds_arr = std::make_unique<json::array> (); - if (const char *verb_str - = diagnostic_event::meaning::maybe_get_verb_str (m.m_verb)) - kinds_arr->append_string (verb_str); - if (const char *noun_str - = diagnostic_event::meaning::maybe_get_noun_str (m.m_noun)) - kinds_arr->append_string (noun_str); - if (const char *property_str - = diagnostic_event::meaning::maybe_get_property_str (m.m_property)) - kinds_arr->append_string (property_str); - return kinds_arr; -} - -/* In "3.11.5 Messages with placeholders": - "Within both plain text and formatted message strings, the characters - "{" and "}" SHALL be represented by the character sequences - "{{" and "}}" respectively." */ - -static std::string -escape_braces (const char *text) -{ - std::string result; - while (char ch = *text++) - switch (ch) - { - case '{': - case '}': - result += ch; - /* Fall through. */ - default: - result += ch; - break; - } - return result; -} - -static void -set_string_property_escaping_braces (json::object &obj, - const char *property_name, - const char *value) -{ - std::string escaped (escape_braces (value)); - obj.set_string (property_name, escaped.c_str ()); -} - -/* Make a "message" object (SARIF v2.1.0 section 3.11) for MSG. */ - -std::unique_ptr<sarif_message> -sarif_builder::make_message_object (const char *msg) const -{ - auto message_obj = std::make_unique<sarif_message> (); - - /* "text" property (SARIF v2.1.0 section 3.11.8). */ - set_string_property_escaping_braces (*message_obj, - "text", msg); - - return message_obj; -} - -/* Make a "message" object (SARIF v2.1.0 section 3.11) for DIAGRAM. - We emit the diagram as a code block within the Markdown part - of the message. */ - -std::unique_ptr<sarif_message> -sarif_builder::make_message_object_for_diagram (const diagnostic_diagram &diagram) -{ - auto message_obj = std::make_unique<sarif_message> (); - - /* "text" property (SARIF v2.1.0 section 3.11.8). */ - set_string_property_escaping_braces (*message_obj, - "text", diagram.get_alt_text ()); - - pretty_printer *const pp = m_printer; - char *saved_prefix = pp_take_prefix (pp); - pp_set_prefix (pp, nullptr); - - /* "To produce a code block in Markdown, simply indent every line of - the block by at least 4 spaces or 1 tab." - Here we use 4 spaces. */ - diagram.get_canvas ().print_to_pp (pp, " "); - pp_set_prefix (pp, saved_prefix); - - /* "markdown" property (SARIF v2.1.0 section 3.11.9). */ - set_string_property_escaping_braces (*message_obj, - "markdown", pp_formatted_text (pp)); - - pp_clear_output_area (pp); - - return message_obj; -} - -/* Make a "multiformatMessageString object" (SARIF v2.1.0 section 3.12) - for MSG. */ - -std::unique_ptr<sarif_multiformat_message_string> -sarif_builder::make_multiformat_message_string (const char *msg) const -{ - auto message_obj = std::make_unique<sarif_multiformat_message_string> (); - - /* "text" property (SARIF v2.1.0 section 3.12.3). */ - set_string_property_escaping_braces (*message_obj, - "text", msg); - - return message_obj; -} - -/* Convert VERSION to a value for the "$schema" property - of a "sarifLog" object (SARIF v2.1.0 section 3.13.3). */ - -static const char * -sarif_version_to_url (enum sarif_version version) -{ - switch (version) - { - default: - gcc_unreachable (); - case sarif_version::v2_1_0: - return "https://docs.oasis-open.org/sarif/sarif/v2.1.0/errata01/os/schemas/sarif-schema-2.1.0.json"; - case sarif_version::v2_2_prerelease_2024_08_08: - 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"; - } -} - -/* Convert VERSION to a value for the "version" property - of a "sarifLog" object (SARIF v2.1.0 section 3.13.2). */ - -static const char * -sarif_version_to_property (enum sarif_version version) -{ - switch (version) - { - default: - gcc_unreachable (); - case sarif_version::v2_1_0: - return "2.1.0"; - case sarif_version::v2_2_prerelease_2024_08_08: - /* I would have used "2.2-prerelease-2024-08-08", - but the schema only accepts "2.2". */ - return "2.2"; - } -} - -/* Make a top-level "sarifLog" object (SARIF v2.1.0 section 3.13). */ - -std::unique_ptr<sarif_log> -sarif_builder:: -make_top_level_object (std::unique_ptr<sarif_invocation> invocation_obj, - std::unique_ptr<json::array> results) -{ - auto log_obj = std::make_unique<sarif_log> (); - - /* "$schema" property (SARIF v2.1.0 section 3.13.3) . */ - log_obj->set_string ("$schema", sarif_version_to_url (get_version ())); - - /* "version" property (SARIF v2.1.0 section 3.13.2). */ - log_obj->set_string ("version", sarif_version_to_property (get_version ())); - - /* "runs" property (SARIF v2.1.0 section 3.13.4). */ - auto run_arr = std::make_unique<json::array> (); - auto run_obj = make_run_object (std::move (invocation_obj), - std::move (results)); - run_arr->append<sarif_run> (std::move (run_obj)); - log_obj->set<json::array> ("runs", std::move (run_arr)); - - return log_obj; -} - -/* Make a "run" object (SARIF v2.1.0 section 3.14). */ - -std::unique_ptr<sarif_run> -sarif_builder:: -make_run_object (std::unique_ptr<sarif_invocation> invocation_obj, - std::unique_ptr<json::array> results) -{ - auto run_obj = std::make_unique<sarif_run> (); - - /* "tool" property (SARIF v2.1.0 section 3.14.6). */ - run_obj->set<sarif_tool> ("tool", make_tool_object ()); - - /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */ - if (auto taxonomies_arr = maybe_make_taxonomies_array ()) - run_obj->set<json::array> ("taxonomies", std::move (taxonomies_arr)); - - /* "invocations" property (SARIF v2.1.0 section 3.14.11). */ - { - auto invocations_arr = std::make_unique<json::array> (); - invocations_arr->append (std::move (invocation_obj)); - run_obj->set<json::array> ("invocations", std::move (invocations_arr)); - } - - /* "originalUriBaseIds (SARIF v2.1.0 section 3.14.14). */ - if (m_seen_any_relative_paths) - { - auto orig_uri_base_ids = std::make_unique<json::object> (); - orig_uri_base_ids->set<sarif_artifact_location> - (PWD_PROPERTY_NAME, make_artifact_location_object_for_pwd ()); - run_obj->set<json::object> ("originalUriBaseIds", - std::move (orig_uri_base_ids)); - } - - /* "artifacts" property (SARIF v2.1.0 section 3.14.15). */ - auto artifacts_arr = std::make_unique<json::array> (); - for (auto iter : m_filename_to_artifact_map) - { - sarif_artifact *artifact_obj = iter.second; - if (artifact_obj->embed_contents_p ()) - artifact_obj->populate_contents (*this); - artifact_obj->populate_roles (); - artifacts_arr->append (artifact_obj); - } - run_obj->set<json::array> ("artifacts", std::move (artifacts_arr)); - m_filename_to_artifact_map.empty (); - - /* "results" property (SARIF v2.1.0 section 3.14.23). */ - run_obj->set<json::array> ("results", std::move (results)); - - /* "logicalLocations" property (SARIF v2.1.0 3.14.17). */ - if (m_cached_logical_locs->size () > 0) - { - m_cached_logical_locs->add_explicit_index_values (); - run_obj->set<json::array> ("logicalLocations", - std::move (m_cached_logical_locs)); - } - - return run_obj; -} - -/* Make a "tool" object (SARIF v2.1.0 section 3.18). */ - -std::unique_ptr<sarif_tool> -sarif_builder::make_tool_object () -{ - auto tool_obj = std::make_unique<sarif_tool> (); - - /* "driver" property (SARIF v2.1.0 section 3.18.2). */ - tool_obj->set<sarif_tool_component> ("driver", - make_driver_tool_component_object ()); - - /* Report plugins via the "extensions" property - (SARIF v2.1.0 section 3.18.3). */ - if (auto client_data_hooks = m_context.get_client_data_hooks ()) - if (const client_version_info *vinfo - = client_data_hooks->get_any_version_info ()) - { - class my_plugin_visitor : public client_version_info :: plugin_visitor - { - public: - void on_plugin (const diagnostic_client_plugin_info &p) final override - { - /* Create a "toolComponent" object (SARIF v2.1.0 section 3.19) - for the plugin. */ - auto plugin_obj = std::make_unique<sarif_tool_component> (); - - /* "name" property (SARIF v2.1.0 section 3.19.8). */ - if (const char *short_name = p.get_short_name ()) - plugin_obj->set_string ("name", short_name); - - /* "fullName" property (SARIF v2.1.0 section 3.19.9). */ - if (const char *full_name = p.get_full_name ()) - plugin_obj->set_string ("fullName", full_name); - - /* "version" property (SARIF v2.1.0 section 3.19.13). */ - if (const char *version = p.get_version ()) - plugin_obj->set_string ("version", version); - - m_plugin_objs.push_back (std::move (plugin_obj)); - } - std::vector<std::unique_ptr<sarif_tool_component>> m_plugin_objs; - }; - my_plugin_visitor v; - vinfo->for_each_plugin (v); - if (v.m_plugin_objs.size () > 0) - { - auto extensions_arr = std::make_unique<json::array> (); - for (auto &iter : v.m_plugin_objs) - extensions_arr->append<sarif_tool_component> (std::move (iter)); - tool_obj->set<json::array> ("extensions", - std::move (extensions_arr)); - } - } - - /* Perhaps we could also show GMP, MPFR, MPC, isl versions as other - "extensions" (see toplev.cc: print_version). */ - - return tool_obj; -} - -/* Make a "toolComponent" object (SARIF v2.1.0 section 3.19) for what SARIF - calls the "driver" (see SARIF v2.1.0 section 3.18.1). */ - -std::unique_ptr<sarif_tool_component> -sarif_builder::make_driver_tool_component_object () -{ - auto driver_obj = std::make_unique<sarif_tool_component> (); - - if (auto client_data_hooks = m_context.get_client_data_hooks ()) - if (const client_version_info *vinfo - = client_data_hooks->get_any_version_info ()) - { - /* "name" property (SARIF v2.1.0 section 3.19.8). */ - if (const char *name = vinfo->get_tool_name ()) - driver_obj->set_string ("name", name); - - /* "fullName" property (SARIF v2.1.0 section 3.19.9). */ - if (char *full_name = vinfo->maybe_make_full_name ()) - { - driver_obj->set_string ("fullName", full_name); - free (full_name); - } - - /* "version" property (SARIF v2.1.0 section 3.19.13). */ - if (const char *version = vinfo->get_version_string ()) - driver_obj->set_string ("version", version); - - /* "informationUri" property (SARIF v2.1.0 section 3.19.17). */ - if (char *version_url = vinfo->maybe_make_version_url ()) - { - driver_obj->set_string ("informationUri", version_url); - free (version_url); - } - } - - /* "rules" property (SARIF v2.1.0 section 3.19.23). */ - driver_obj->set<json::array> ("rules", std::move (m_rules_arr)); - - return driver_obj; -} - -/* If we've seen any CWE IDs, make an array for the "taxonomies" property - (SARIF v2.1.0 section 3.14.8) of a run object, containing a single - "toolComponent" (3.19) as per 3.19.3, representing the CWE. - - Otherwise return nullptr. */ - -std::unique_ptr<json::array> -sarif_builder::maybe_make_taxonomies_array () const -{ - auto cwe_obj = maybe_make_cwe_taxonomy_object (); - if (!cwe_obj) - return nullptr; - - /* "taxonomies" property (SARIF v2.1.0 section 3.14.8). */ - auto taxonomies_arr = std::make_unique<json::array> (); - taxonomies_arr->append<sarif_tool_component> (std::move (cwe_obj)); - return taxonomies_arr; -} - -/* If we've seen any CWE IDs, make a "toolComponent" object - (SARIF v2.1.0 section 3.19) representing the CWE taxonomy, as per 3.19.3. - Populate the "taxa" property with all of the CWE IDs in m_cwe_id_set. - - Otherwise return nullptr. */ - -std::unique_ptr<sarif_tool_component> -sarif_builder::maybe_make_cwe_taxonomy_object () const -{ - if (m_cwe_id_set.is_empty ()) - return nullptr; - - auto taxonomy_obj = std::make_unique<sarif_tool_component> (); - - /* "name" property (SARIF v2.1.0 section 3.19.8). */ - taxonomy_obj->set_string ("name", "CWE"); - - /* "version" property (SARIF v2.1.0 section 3.19.13). */ - taxonomy_obj->set_string ("version", "4.7"); - - /* "organization" property (SARIF v2.1.0 section 3.19.18). */ - taxonomy_obj->set_string ("organization", "MITRE"); - - /* "shortDescription" property (SARIF v2.1.0 section 3.19.19). */ - taxonomy_obj->set<sarif_multiformat_message_string> - ("shortDescription", - make_multiformat_message_string ("The MITRE" - " Common Weakness Enumeration")); - - /* "taxa" property (SARIF v2.1.0 3.section 3.19.25). */ - auto taxa_arr = std::make_unique<json::array> (); - for (auto cwe_id : m_cwe_id_set) - taxa_arr->append<sarif_reporting_descriptor> - (make_reporting_descriptor_object_for_cwe_id (cwe_id)); - taxonomy_obj->set<json::array> ("taxa", std::move (taxa_arr)); - - return taxonomy_obj; -} - -/* Ensure that we have an "artifact" object (SARIF v2.1.0 section 3.24) - for FILENAME, adding it to m_filename_to_artifact_map if not already - found, and adding ROLE to it. - If EMBED_CONTENTS is true, then flag that we will attempt to embed the - contents of this artifact when writing it out. */ - -sarif_artifact & -sarif_builder::get_or_create_artifact (const char *filename, - enum diagnostic_artifact_role role, - bool embed_contents) -{ - if (auto *slot = m_filename_to_artifact_map.get (filename)) - { - (*slot)->add_role (role, embed_contents); - return **slot; - } - - sarif_artifact *artifact_obj = new sarif_artifact (filename); - artifact_obj->add_role (role, embed_contents); - m_filename_to_artifact_map.put (filename, artifact_obj); - - /* "location" property (SARIF v2.1.0 section 3.24.2). */ - artifact_obj->set<sarif_artifact_location> - ("location", make_artifact_location_object (filename)); - - /* "sourceLanguage" property (SARIF v2.1.0 section 3.24.10). */ - switch (role) - { - default: - gcc_unreachable (); - case diagnostic_artifact_role::analysis_target: - case diagnostic_artifact_role::result_file: - case diagnostic_artifact_role::scanned_file: - case diagnostic_artifact_role::traced_file: - /* Assume that these are in the source language. */ - if (auto client_data_hooks = m_context.get_client_data_hooks ()) - if (const char *source_lang - = client_data_hooks->maybe_get_sarif_source_language (filename)) - artifact_obj->set_string ("sourceLanguage", source_lang); - break; - - case diagnostic_artifact_role::debug_output_file: - /* Assume that these are not in the source language. */ - break; - } - - return *artifact_obj; -} - -/* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for the - full contents of FILENAME. */ - -std::unique_ptr<sarif_artifact_content> -sarif_builder::maybe_make_artifact_content_object (const char *filename) const -{ - /* Let input.cc handle any charset conversion. */ - char_span utf8_content - = m_context.get_file_cache ().get_source_file_content (filename); - if (!utf8_content) - return nullptr; - - /* Don't add it if it's not valid UTF-8. */ - if (!cpp_valid_utf8_p(utf8_content.get_buffer (), utf8_content.length ())) - return nullptr; - - auto artifact_content_obj = std::make_unique<sarif_artifact_content> (); - artifact_content_obj->set<json::string> - ("text", - std::make_unique <json::string> (utf8_content.get_buffer (), - utf8_content.length ())); - return artifact_content_obj; -} - -/* Attempt to read the given range of lines from FILENAME; return - a freshly-allocated 0-terminated buffer containing them, or nullptr. */ - -char * -sarif_builder::get_source_lines (const char *filename, - int start_line, - int end_line) const -{ - auto_vec<char> result; - - for (int line = start_line; line <= end_line; line++) - { - char_span line_content - = m_context.get_file_cache ().get_source_line (filename, line); - if (!line_content.get_buffer ()) - return nullptr; - result.reserve (line_content.length () + 1); - for (size_t i = 0; i < line_content.length (); i++) - result.quick_push (line_content[i]); - result.quick_push ('\n'); - } - result.safe_push ('\0'); - - return xstrdup (result.address ()); -} - -/* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for the given - run of lines within FILENAME (including the endpoints). - If R is non-NULL, use it to potentially set the "rendered" - property (3.3.4). */ - -std::unique_ptr<sarif_artifact_content> -sarif_builder:: -maybe_make_artifact_content_object (const char *filename, - int start_line, - int end_line, - const content_renderer *r) const -{ - char *text_utf8 = get_source_lines (filename, start_line, end_line); - - if (!text_utf8) - return nullptr; - - /* Don't add it if it's not valid UTF-8. */ - if (!cpp_valid_utf8_p(text_utf8, strlen(text_utf8))) - { - free (text_utf8); - return nullptr; - } - - auto artifact_content_obj = std::make_unique<sarif_artifact_content> (); - artifact_content_obj->set_string ("text", text_utf8); - free (text_utf8); - - /* 3.3.4 "rendered" property. */ - if (r) - if (std::unique_ptr<sarif_multiformat_message_string> rendered - = r->render (*this)) - artifact_content_obj->set ("rendered", std::move (rendered)); - - return artifact_content_obj; -} - -/* Make a "fix" object (SARIF v2.1.0 section 3.55) for RICHLOC. */ - -std::unique_ptr<sarif_fix> -sarif_builder::make_fix_object (const rich_location &richloc) -{ - auto fix_obj = std::make_unique<sarif_fix> (); - - /* "artifactChanges" property (SARIF v2.1.0 section 3.55.3). */ - /* We assume that all fix-it hints in RICHLOC affect the same file. */ - auto artifact_change_arr = std::make_unique<json::array> (); - artifact_change_arr->append<sarif_artifact_change> - (make_artifact_change_object (richloc)); - fix_obj->set<json::array> ("artifactChanges", - std::move (artifact_change_arr)); - - return fix_obj; -} - -/* Make an "artifactChange" object (SARIF v2.1.0 section 3.56) for RICHLOC. */ - -std::unique_ptr<sarif_artifact_change> -sarif_builder::make_artifact_change_object (const rich_location &richloc) -{ - auto artifact_change_obj = std::make_unique<sarif_artifact_change> (); - - /* "artifactLocation" property (SARIF v2.1.0 section 3.56.2). */ - artifact_change_obj->set<sarif_artifact_location> - ("artifactLocation", - make_artifact_location_object (richloc.get_loc ())); - - /* "replacements" property (SARIF v2.1.0 section 3.56.3). */ - auto replacement_arr = std::make_unique<json::array> (); - for (unsigned int i = 0; i < richloc.get_num_fixit_hints (); i++) - { - const fixit_hint *hint = richloc.get_fixit_hint (i); - replacement_arr->append<sarif_replacement> - (make_replacement_object (*hint)); - } - artifact_change_obj->set<json::array> ("replacements", - std::move (replacement_arr)); - - return artifact_change_obj; -} - -/* Make a "replacement" object (SARIF v2.1.0 section 3.57) for HINT. */ - -std::unique_ptr<sarif_replacement> -sarif_builder::make_replacement_object (const fixit_hint &hint) const -{ - auto replacement_obj = std::make_unique<sarif_replacement> (); - - /* "deletedRegion" property (SARIF v2.1.0 section 3.57.3). */ - replacement_obj->set<sarif_region> ("deletedRegion", - make_region_object_for_hint (hint)); - - /* "insertedContent" property (SARIF v2.1.0 section 3.57.4). */ - replacement_obj->set<sarif_artifact_content> - ("insertedContent", - make_artifact_content_object (hint.get_string ())); - - return replacement_obj; -} - -/* Make an "artifactContent" object (SARIF v2.1.0 section 3.3) for TEXT. */ - -std::unique_ptr<sarif_artifact_content> -sarif_builder::make_artifact_content_object (const char *text) const -{ - auto content_obj = std::make_unique<sarif_artifact_content> (); - - /* "text" property (SARIF v2.1.0 section 3.3.2). */ - content_obj->set_string ("text", text); - - return content_obj; -} - -/* class diagnostic_sarif_format_buffer : public diagnostic_per_format_buffer. */ - -void -diagnostic_sarif_format_buffer::dump (FILE *out, int indent) const -{ - fprintf (out, "%*sdiagnostic_sarif_format_buffer:\n", indent, ""); - int idx = 0; - for (auto &result : m_results) - { - fprintf (out, "%*sresult[%i]:\n", indent + 2, "", idx); - result->dump (out, true); - fprintf (out, "\n"); - ++idx; - } -} - -bool -diagnostic_sarif_format_buffer::empty_p () const -{ - return m_results.empty (); -} - -void -diagnostic_sarif_format_buffer::move_to (diagnostic_per_format_buffer &base) -{ - diagnostic_sarif_format_buffer &dest - = static_cast<diagnostic_sarif_format_buffer &> (base); - for (auto &&result : m_results) - dest.m_results.push_back (std::move (result)); - m_results.clear (); -} - -void -diagnostic_sarif_format_buffer::clear () -{ - m_results.clear (); -} - -void -diagnostic_sarif_format_buffer::flush () -{ - for (auto &&result : m_results) - { - result->process_worklist (m_builder); - m_builder.m_results_array->append<sarif_result> (std::move (result)); - } - m_results.clear (); -} - -class sarif_output_format : public diagnostic_output_format -{ -public: - ~sarif_output_format () - { - /* Any sarifResult objects should have been handled by now. - If not, then something's gone wrong with diagnostic - groupings. */ - std::unique_ptr<sarif_result> pending_result - = m_builder.take_current_result (); - gcc_assert (!pending_result); - } - - void dump (FILE *out, int indent) const override - { - fprintf (out, "%*ssarif_output_format\n", indent, ""); - diagnostic_output_format::dump (out, indent); - } - - std::unique_ptr<diagnostic_per_format_buffer> - make_per_format_buffer () final override - { - return std::make_unique<diagnostic_sarif_format_buffer> (m_builder); - } - void set_buffer (diagnostic_per_format_buffer *base_buffer) final override - { - diagnostic_sarif_format_buffer *buffer - = static_cast<diagnostic_sarif_format_buffer *> (base_buffer); - m_buffer = buffer; - } - - bool follows_reference_printer_p () const final override - { - return false; - } - - void update_printer () final override - { - m_printer = m_context.clone_printer (); - - /* Don't colorize the text. */ - pp_show_color (m_printer.get ()) = false; - - /* No textual URLs. */ - m_printer->set_url_format (URL_FORMAT_NONE); - - /* Use builder's token printer. */ - get_printer ()->set_token_printer (&m_builder.get_token_printer ()); - - /* Update the builder to use the new printer. */ - m_builder.set_printer (*get_printer ()); - } - - void on_begin_group () final override - { - /* No-op, */ - } - void on_end_group () final override - { - m_builder.end_group (); - } - void - on_report_diagnostic (const diagnostic_info &diagnostic, - diagnostic_t orig_diag_kind) final override - { - m_builder.on_report_diagnostic (diagnostic, orig_diag_kind, m_buffer); - } - void on_diagram (const diagnostic_diagram &diagram) final override - { - m_builder.emit_diagram (diagram); - } - void after_diagnostic (const diagnostic_info &) final override - { - /* No-op. */ - } - - sarif_builder &get_builder () { return m_builder; } - - size_t num_results () const { return m_builder.num_results (); } - sarif_result &get_result (size_t idx) { return m_builder.get_result (idx); } - -protected: - sarif_output_format (diagnostic_context &context, - const line_maps *line_maps, - const char *main_input_filename_, - std::unique_ptr<sarif_serialization_format> serialization_format, - const sarif_generation_options &sarif_gen_opts) - : diagnostic_output_format (context), - m_builder (context, *get_printer (), line_maps, main_input_filename_, - std::move (serialization_format), sarif_gen_opts), - m_buffer (nullptr) - {} - - sarif_builder m_builder; - diagnostic_sarif_format_buffer *m_buffer; -}; - -class sarif_stream_output_format : public sarif_output_format -{ -public: - sarif_stream_output_format (diagnostic_context &context, - const line_maps *line_maps, - const char *main_input_filename_, - std::unique_ptr<sarif_serialization_format> serialization_format, - const sarif_generation_options &sarif_gen_opts, - FILE *stream) - : sarif_output_format (context, line_maps, main_input_filename_, - std::move (serialization_format), sarif_gen_opts), - m_stream (stream) - { - } - ~sarif_stream_output_format () - { - m_builder.flush_to_file (m_stream); - } - bool machine_readable_stderr_p () const final override - { - return m_stream == stderr; - } -private: - FILE *m_stream; -}; - -class sarif_file_output_format : public sarif_output_format -{ -public: - sarif_file_output_format (diagnostic_context &context, - const line_maps *line_maps, - const char *main_input_filename_, - std::unique_ptr<sarif_serialization_format> serialization_format, - const sarif_generation_options &sarif_gen_opts, - diagnostic_output_file output_file) - : sarif_output_format (context, line_maps, main_input_filename_, - std::move (serialization_format), sarif_gen_opts), - m_output_file (std::move (output_file)) - { - gcc_assert (m_output_file.get_open_file ()); - gcc_assert (m_output_file.get_filename ()); - } - ~sarif_file_output_format () - { - m_builder.flush_to_file (m_output_file.get_open_file ()); - } - void dump (FILE *out, int indent) const override - { - fprintf (out, "%*ssarif_file_output_format: %s\n", - indent, "", - m_output_file.get_filename ()); - diagnostic_output_format::dump (out, indent); - } - bool machine_readable_stderr_p () const final override - { - return false; - } - -private: - diagnostic_output_file m_output_file; -}; - -/* Print the start of an embedded link to PP, as per 3.11.6. */ - -static void -sarif_begin_embedded_link (pretty_printer *pp) -{ - pp_character (pp, '['); -} - -/* Print the end of an embedded link to PP, as per 3.11.6. */ - -static void -sarif_end_embedded_link (pretty_printer *pp, - const char *url) -{ - pp_string (pp, "]("); - /* TODO: does the URI need escaping? - See https://github.com/oasis-tcs/sarif-spec/issues/657 */ - pp_string (pp, url); - pp_character (pp, ')'); -} - -/* class sarif_token_printer : public token_printer. */ - -/* Implementation of pretty_printer::token_printer for SARIF output. - Emit URLs as per 3.11.6 ("Messages with embedded links"). */ - -void -sarif_builder::sarif_token_printer::print_tokens (pretty_printer *pp, - const pp_token_list &tokens) -{ - /* Convert to text, possibly with colorization, URLs, etc. */ - label_text current_url; - for (auto iter = tokens.m_first; iter; iter = iter->m_next) - switch (iter->m_kind) - { - default: - gcc_unreachable (); - - case pp_token::kind::text: - { - const pp_token_text *sub = as_a <const pp_token_text *> (iter); - const char * const str = sub->m_value.get (); - if (current_url.get ()) - { - /* Write iter->m_value, but escaping any - escaped link characters as per 3.11.6. */ - for (const char *ptr = str; *ptr; ptr++) - { - const char ch = *ptr; - switch (ch) - { - default: - pp_character (pp, ch); - break; - case '\\': - case '[': - case ']': - pp_character (pp, '\\'); - pp_character (pp, ch); - break; - } - } - } - else - /* TODO: is other escaping needed? (e.g. of '[') - See https://github.com/oasis-tcs/sarif-spec/issues/658 */ - pp_string (pp, str); - } - break; - - case pp_token::kind::begin_color: - case pp_token::kind::end_color: - /* These are no-ops. */ - break; - - case pp_token::kind::begin_quote: - pp_begin_quote (pp, pp_show_color (pp)); - break; - case pp_token::kind::end_quote: - pp_end_quote (pp, pp_show_color (pp)); - break; - - /* Emit URLs as per 3.11.6 ("Messages with embedded links"). */ - case pp_token::kind::begin_url: - { - pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter); - sarif_begin_embedded_link (pp); - current_url = std::move (sub->m_value); - } - break; - case pp_token::kind::end_url: - gcc_assert (current_url.get ()); - sarif_end_embedded_link (pp, current_url.get ()); - current_url = label_text::borrow (nullptr); - break; - - case pp_token::kind::event_id: - { - pp_token_event_id *sub = as_a <pp_token_event_id *> (iter); - gcc_assert (sub->m_event_id.known_p ()); - const sarif_code_flow *code_flow - = m_builder.get_code_flow_for_event_ids (); - label_text url = make_sarif_url_for_event (code_flow, - sub->m_event_id); - if (url.get ()) - sarif_begin_embedded_link (pp); - pp_character (pp, '('); - pp_decimal_int (pp, sub->m_event_id.one_based ()); - pp_character (pp, ')'); - if (url.get ()) - sarif_end_embedded_link (pp, url.get ()); - } - break; - } -} - -/* Populate CONTEXT in preparation for SARIF output (either to stderr, or - to a file). */ - -static void -diagnostic_output_format_init_sarif (diagnostic_context &context, - std::unique_ptr<sarif_output_format> fmt) -{ - fmt->update_printer (); - - context.set_output_format (std::move (fmt)); -} - -/* Populate CONTEXT in preparation for SARIF output to stderr. */ - -void -diagnostic_output_format_init_sarif_stderr (diagnostic_context &context, - const line_maps *line_maps, - const char *main_input_filename_, - bool formatted) -{ - gcc_assert (line_maps); - const sarif_generation_options sarif_gen_opts; - auto serialization - = std::make_unique<sarif_serialization_format_json> (formatted); - diagnostic_output_format_init_sarif - (context, - std::make_unique<sarif_stream_output_format> (context, - line_maps, - main_input_filename_, - std::move (serialization), - sarif_gen_opts, - stderr)); -} - -/* Attempt to open "BASE_FILE_NAME""EXTENSION" for writing. - Return a non-null diagnostic_output_file, - or return a null diagnostic_output_file and complain to CONTEXT - using LINE_MAPS. */ - -diagnostic_output_file -diagnostic_output_file::try_to_open (diagnostic_context &context, - line_maps *line_maps, - const char *base_file_name, - const char *extension, - bool is_binary) -{ - gcc_assert (extension); - gcc_assert (extension[0] == '.'); - - if (!base_file_name) - { - rich_location richloc (line_maps, UNKNOWN_LOCATION); - context.emit_diagnostic_with_group - (DK_ERROR, richloc, nullptr, 0, - "unable to determine filename for SARIF output"); - return diagnostic_output_file (); - } - - label_text filename = label_text::take (concat (base_file_name, - extension, - nullptr)); - FILE *outf = fopen (filename.get (), is_binary ? "wb" : "w"); - if (!outf) - { - rich_location richloc (line_maps, UNKNOWN_LOCATION); - context.emit_diagnostic_with_group - (DK_ERROR, richloc, nullptr, 0, - "unable to open %qs for diagnostic output: %m", - filename.get ()); - return diagnostic_output_file (); - } - return diagnostic_output_file (outf, true, std::move (filename)); -} - -/* Attempt to open BASE_FILE_NAME.sarif for writing JSON. - Return a non-null diagnostic_output_file, - or return a null diagnostic_output_file and complain to CONTEXT - using LINE_MAPS. */ - -diagnostic_output_file -diagnostic_output_format_open_sarif_file (diagnostic_context &context, - line_maps *line_maps, - const char *base_file_name, - enum sarif_serialization_kind serialization_kind) -{ - const char *suffix; - bool is_binary; - switch (serialization_kind) - { - default: - gcc_unreachable (); - case sarif_serialization_kind::json: - suffix = ".sarif"; - is_binary = false; - break; - } - - return diagnostic_output_file::try_to_open (context, - line_maps, - base_file_name, - suffix, - is_binary); -} - -/* Populate CONTEXT in preparation for SARIF output to a file named - BASE_FILE_NAME.sarif. */ - -void -diagnostic_output_format_init_sarif_file (diagnostic_context &context, - line_maps *line_maps, - const char *main_input_filename_, - bool formatted, - const char *base_file_name) -{ - gcc_assert (line_maps); - - diagnostic_output_file output_file - = diagnostic_output_format_open_sarif_file (context, - line_maps, - base_file_name, - sarif_serialization_kind::json); - auto serialization - = std::make_unique<sarif_serialization_format_json> (formatted); - - const sarif_generation_options sarif_gen_opts; - diagnostic_output_format_init_sarif - (context, - std::make_unique<sarif_file_output_format> (context, - line_maps, - main_input_filename_, - std::move (serialization), - sarif_gen_opts, - std::move (output_file))); -} - -/* Populate CONTEXT in preparation for SARIF output to STREAM. */ - -void -diagnostic_output_format_init_sarif_stream (diagnostic_context &context, - const line_maps *line_maps, - const char *main_input_filename_, - bool formatted, - FILE *stream) -{ - gcc_assert (line_maps); - const sarif_generation_options sarif_gen_opts; - auto serialization - = std::make_unique<sarif_serialization_format_json> (formatted); - diagnostic_output_format_init_sarif - (context, - std::make_unique<sarif_stream_output_format> (context, - line_maps, - main_input_filename_, - std::move (serialization), - sarif_gen_opts, - stream)); -} - -std::unique_ptr<diagnostic_output_format> -make_sarif_sink (diagnostic_context &context, - const line_maps &line_maps, - const char *main_input_filename_, - std::unique_ptr<sarif_serialization_format> serialization, - const sarif_generation_options &sarif_gen_opts, - diagnostic_output_file output_file) -{ - auto sink - = std::make_unique<sarif_file_output_format> (context, - &line_maps, - main_input_filename_, - std::move (serialization), - sarif_gen_opts, - std::move (output_file)); - sink->update_printer (); - return sink; -} - -// struct sarif_generation_options - -sarif_generation_options::sarif_generation_options () -: m_version (sarif_version::v2_1_0) -{ -} - -#if CHECKING_P - -namespace selftest { - -static void -test_sarif_array_of_unique_1 () -{ - sarif_array_of_unique<json::string> arr; - - ASSERT_EQ (arr.length (), 0); - - { - size_t idx = arr.append_uniquely (std::make_unique<json::string> ("foo")); - ASSERT_EQ (idx, 0); - ASSERT_EQ (arr.length (), 1); - } - { - size_t idx = arr.append_uniquely (std::make_unique<json::string> ("bar")); - ASSERT_EQ (idx, 1); - ASSERT_EQ (arr.length (), 2); - } - - /* Try adding them again, should be idempotent. */ - { - size_t idx = arr.append_uniquely (std::make_unique<json::string> ("foo")); - ASSERT_EQ (idx, 0); - ASSERT_EQ (arr.length (), 2); - } - { - size_t idx = arr.append_uniquely (std::make_unique<json::string> ("bar")); - ASSERT_EQ (idx, 1); - ASSERT_EQ (arr.length (), 2); - } -} - -static void -test_sarif_array_of_unique_2 () -{ - sarif_array_of_unique<json::object> arr; - - ASSERT_EQ (arr.length (), 0); - - { - auto obj0 = std::make_unique<json::object> (); - size_t idx = arr.append_uniquely (std::move (obj0)); - ASSERT_EQ (idx, 0); - ASSERT_EQ (arr.length (), 1); - - // Attempting to add another empty objects should be idempotent. - idx = arr.append_uniquely (std::make_unique<json::object> ()); - ASSERT_EQ (idx, 0); - ASSERT_EQ (arr.length (), 1); - } - { - auto obj1 = std::make_unique<json::object> (); - obj1->set_string ("foo", "bar"); - size_t idx = arr.append_uniquely (std::move (obj1)); - ASSERT_EQ (idx, 1); - ASSERT_EQ (arr.length (), 2); - - // Attempting to add an equivalent object should be idempotent. - auto other = std::make_unique<json::object> (); - other->set_string ("foo", "bar"); - idx = arr.append_uniquely (std::move (other)); - ASSERT_EQ (idx, 1); - ASSERT_EQ (arr.length (), 2); - } - - // Verify behavior of add_explicit_index_values. - arr.add_explicit_index_values (); - ASSERT_JSON_INT_PROPERTY_EQ (arr[0], "index", 0); - ASSERT_JSON_INT_PROPERTY_EQ (arr[1], "index", 1); -} - -/* A subclass of sarif_output_format for writing selftests. - The JSON output is cached internally, rather than written - out to a file. */ - -class test_sarif_diagnostic_context : public test_diagnostic_context -{ -public: - test_sarif_diagnostic_context (const char *main_input_filename, - const sarif_generation_options &sarif_gen_opts) - { - auto format = std::make_unique<buffered_output_format> (*this, - line_table, - main_input_filename, - true, - sarif_gen_opts); - m_format = format.get (); // borrowed - diagnostic_output_format_init_sarif (*this, std::move (format)); - } - - std::unique_ptr<sarif_log> flush_to_object () - { - return m_format->flush_to_object (); - } - - size_t num_results () const { return m_format->num_results (); } - sarif_result &get_result (size_t idx) { return m_format->get_result (idx); } - -private: - class buffered_output_format : public sarif_output_format - { - public: - buffered_output_format (diagnostic_context &context, - const line_maps *line_maps, - const char *main_input_filename_, - bool formatted, - const sarif_generation_options &sarif_gen_opts) - : sarif_output_format (context, line_maps, main_input_filename_, - std::make_unique<sarif_serialization_format_json> - (formatted), - sarif_gen_opts) - { - } - bool machine_readable_stderr_p () const final override - { - return false; - } - std::unique_ptr<sarif_log> flush_to_object () - { - return m_builder.flush_to_object (); - } - }; - - buffered_output_format *m_format; // borrowed -}; - -/* Test making a sarif_location for a complex rich_location - with labels and escape-on-output. */ - -static void -test_make_location_object (const sarif_generation_options &sarif_gen_opts, - const line_table_case &case_) -{ - diagnostic_show_locus_fixture_one_liner_utf8 f (case_); - location_t line_end = linemap_position_for_column (line_table, 31); - - /* Don't attempt to run the tests if column data might be unavailable. */ - if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) - return; - - test_diagnostic_context dc; - pretty_printer pp; - sarif_builder builder - (dc, pp, line_table, "MAIN_INPUT_FILENAME", - std::make_unique<sarif_serialization_format_json> (true), - sarif_gen_opts); - - /* These "columns" are byte offsets, whereas later on the columns - in the generated SARIF use sarif_builder::get_sarif_column and - thus respect tabs, encoding. */ - const location_t foo - = make_location (linemap_position_for_column (line_table, 1), - linemap_position_for_column (line_table, 1), - linemap_position_for_column (line_table, 8)); - const location_t bar - = make_location (linemap_position_for_column (line_table, 12), - linemap_position_for_column (line_table, 12), - linemap_position_for_column (line_table, 17)); - const location_t field - = make_location (linemap_position_for_column (line_table, 19), - linemap_position_for_column (line_table, 19), - linemap_position_for_column (line_table, 30)); - - text_range_label label0 ("label0"); - text_range_label label1 ("label1"); - text_range_label label2 ("label2"); - - rich_location richloc (line_table, foo, &label0, nullptr); - richloc.add_range (bar, SHOW_RANGE_WITHOUT_CARET, &label1); - richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2); - richloc.set_escape_on_output (true); - - sarif_result result (0); - - std::unique_ptr<sarif_location> location_obj - = builder.make_location_object - (result, richloc, logical_location (), - diagnostic_artifact_role::analysis_target); - ASSERT_NE (location_obj, nullptr); - - auto physical_location - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (location_obj.get (), - "physicalLocation"); - { - auto region - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location, "region"); - ASSERT_JSON_INT_PROPERTY_EQ (region, "startLine", 1); - ASSERT_JSON_INT_PROPERTY_EQ (region, "startColumn", 1); - ASSERT_JSON_INT_PROPERTY_EQ (region, "endColumn", 7); - } - { - auto context_region - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location, - "contextRegion"); - ASSERT_JSON_INT_PROPERTY_EQ (context_region, "startLine", 1); - - { - auto snippet - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (context_region, "snippet"); - - /* We expect the snippet's "text" to be a copy of the content. */ - ASSERT_JSON_STRING_PROPERTY_EQ (snippet, "text", f.m_content); - - /* We expect the snippet to have a "rendered" whose "text" has a - pure ASCII escaped copy of the line (with labels, etc). */ - { - auto rendered - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (snippet, "rendered"); - ASSERT_JSON_STRING_PROPERTY_EQ - (rendered, "text", - "1 | <U+1F602>_foo = <U+03C0>_bar.<U+1F602>_field<U+03C0>;\n" - " | ^~~~~~~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~\n" - " | | | |\n" - " | label0 label1 label2\n"); - } - } - } - auto annotations - = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (location_obj.get (), - "annotations"); - ASSERT_EQ (annotations->size (), 3); - { - { - auto a0 = (*annotations)[0]; - ASSERT_JSON_INT_PROPERTY_EQ (a0, "startLine", 1); - ASSERT_JSON_INT_PROPERTY_EQ (a0, "startColumn", 1); - ASSERT_JSON_INT_PROPERTY_EQ (a0, "endColumn", 7); - auto message - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (a0, "message"); - ASSERT_JSON_STRING_PROPERTY_EQ (message, "text", "label0"); - } - { - auto a1 = (*annotations)[1]; - ASSERT_JSON_INT_PROPERTY_EQ (a1, "startLine", 1); - ASSERT_JSON_INT_PROPERTY_EQ (a1, "startColumn", 10); - ASSERT_JSON_INT_PROPERTY_EQ (a1, "endColumn", 15); - auto message - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (a1, "message"); - ASSERT_JSON_STRING_PROPERTY_EQ (message, "text", "label1"); - } - { - auto a2 = (*annotations)[2]; - ASSERT_JSON_INT_PROPERTY_EQ (a2, "startLine", 1); - ASSERT_JSON_INT_PROPERTY_EQ (a2, "startColumn", 16); - ASSERT_JSON_INT_PROPERTY_EQ (a2, "endColumn", 25); - auto message - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (a2, "message"); - ASSERT_JSON_STRING_PROPERTY_EQ (message, "text", "label2"); - } - } -} - -/* Test of reporting a diagnostic at UNKNOWN_LOCATION to a - diagnostic_context and examining the generated sarif_log. - Verify various basic properties. */ - -static void -test_simple_log (const sarif_generation_options &sarif_gen_opts) -{ - test_sarif_diagnostic_context dc ("MAIN_INPUT_FILENAME", sarif_gen_opts); - - rich_location richloc (line_table, UNKNOWN_LOCATION); - dc.report (DK_ERROR, richloc, nullptr, 0, "this is a test: %i", 42); - - auto log_ptr = dc.flush_to_object (); - - // 3.13 sarifLog: - auto log = log_ptr.get (); - const enum sarif_version version = sarif_gen_opts.m_version; - ASSERT_JSON_STRING_PROPERTY_EQ (log, "$schema", - sarif_version_to_url (version)); - ASSERT_JSON_STRING_PROPERTY_EQ (log, "version", - sarif_version_to_property (version)); - - auto runs = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log, "runs"); // 3.13.4 - ASSERT_EQ (runs->size (), 1); - - // 3.14 "run" object: - auto run = (*runs)[0]; - - { - // 3.14.6: - auto tool = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (run, "tool"); - - EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (tool, "driver"); // 3.18.2 - } - - { - // 3.14.11 - auto invocations - = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run, "invocations"); - ASSERT_EQ (invocations->size (), 1); - - { - // 3.20 "invocation" object: - auto invocation = (*invocations)[0]; - - // 3.20.3 arguments property - - // 3.20.7 startTimeUtc property - EXPECT_JSON_OBJECT_WITH_STRING_PROPERTY (invocation, "startTimeUtc"); - - // 3.20.8 endTimeUtc property - EXPECT_JSON_OBJECT_WITH_STRING_PROPERTY (invocation, "endTimeUtc"); - - // 3.20.19 workingDirectory property - { - auto wd_obj - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (invocation, - "workingDirectory"); - EXPECT_JSON_OBJECT_WITH_STRING_PROPERTY (wd_obj, "uri"); - } - - // 3.20.21 toolExecutionNotifications property - auto notifications - = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY - (invocation, "toolExecutionNotifications"); - ASSERT_EQ (notifications->size (), 0); - } - } - - { - // 3.14.15: - auto artifacts = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run, "artifacts"); - ASSERT_EQ (artifacts->size (), 1); - - { - // 3.24 "artifact" object: - auto artifact = (*artifacts)[0]; - - // 3.24.2: - auto location - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (artifact, "location"); - ASSERT_JSON_STRING_PROPERTY_EQ (location, "uri", "MAIN_INPUT_FILENAME"); - - // 3.24.6: - auto roles = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (artifact, "roles"); - ASSERT_EQ (roles->size (), 1); - { - auto role = (*roles)[0]; - ASSERT_JSON_STRING_EQ (role, "analysisTarget"); - } - } - } - - { - // 3.14.23: - auto results = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run, "results"); - ASSERT_EQ (results->size (), 1); - - { - // 3.27 "result" object: - auto result = (*results)[0]; - ASSERT_JSON_STRING_PROPERTY_EQ (result, "ruleId", "error"); - ASSERT_JSON_STRING_PROPERTY_EQ (result, "level", "error"); // 3.27.10 - - { - // 3.27.11: - auto message - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result, "message"); - ASSERT_JSON_STRING_PROPERTY_EQ (message, "text", - "this is a test: 42"); - } - - // 3.27.12: - auto locations - = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (result, "locations"); - ASSERT_EQ (locations->size (), 0); - } - } -} - -/* As above, but with a "real" location_t. */ - -static void -test_simple_log_2 (const sarif_generation_options &sarif_gen_opts, - const line_table_case &case_) -{ - auto_fix_quotes fix_quotes; - - const char *const content - /* 000000000111111 - 123456789012345. */ - = "unsinged int i;\n"; - diagnostic_show_locus_fixture f (case_, content); - location_t line_end = linemap_position_for_column (line_table, 31); - - /* Don't attempt to run the tests if column data might be unavailable. */ - if (line_end > LINE_MAP_MAX_LOCATION_WITH_COLS) - return; - - test_sarif_diagnostic_context dc (f.get_filename (), sarif_gen_opts); - - const location_t typo_loc - = make_location (linemap_position_for_column (line_table, 1), - linemap_position_for_column (line_table, 1), - linemap_position_for_column (line_table, 8)); - - rich_location richloc (line_table, typo_loc); - dc.report (DK_ERROR, richloc, nullptr, 0, - "did you misspell %qs again?", - "unsigned"); - - auto log_ptr = dc.flush_to_object (); - - // 3.13 sarifLog: - auto log = log_ptr.get (); - - auto runs = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log, "runs"); // 3.13.4 - ASSERT_EQ (runs->size (), 1); - - // 3.14 "run" object: - auto run = (*runs)[0]; - - { - // 3.14.23: - auto results = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run, "results"); - ASSERT_EQ (results->size (), 1); - - { - // 3.27 "result" object: - auto result = (*results)[0]; - ASSERT_JSON_STRING_PROPERTY_EQ (result, "ruleId", "error"); - ASSERT_JSON_STRING_PROPERTY_EQ (result, "level", "error"); // 3.27.10 - - { - // 3.27.11: - auto message - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result, "message"); - ASSERT_JSON_STRING_PROPERTY_EQ (message, "text", - "did you misspell `unsigned' again?"); - } - - // 3.27.12: - auto locations - = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (result, "locations"); - ASSERT_EQ (locations->size (), 1); - - { - // 3.28 "location" object: - auto location = (*locations)[0]; - - auto physical_location - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (location, - "physicalLocation"); - { - auto region - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location, - "region"); - ASSERT_JSON_INT_PROPERTY_EQ (region, "startLine", 1); - ASSERT_JSON_INT_PROPERTY_EQ (region, "startColumn", 1); - ASSERT_JSON_INT_PROPERTY_EQ (region, "endColumn", 9); - } - { - auto context_region - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (physical_location, - "contextRegion"); - ASSERT_JSON_INT_PROPERTY_EQ (context_region, "startLine", 1); - - { - auto snippet - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (context_region, - "snippet"); - - /* We expect the snippet's "text" to be a copy of the content. */ - ASSERT_JSON_STRING_PROPERTY_EQ (snippet, "text", f.m_content); - } - } - } - } - } -} - -/* Assuming that a single diagnostic has been emitted within - LOG, get a json::object for the result object. */ - -static const json::object * -get_result_from_log (const sarif_log *log) -{ - auto runs = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (log, "runs"); // 3.13.4 - ASSERT_EQ (runs->size (), 1); - - // 3.14 "run" object: - auto run = (*runs)[0]; - - // 3.14.23: - auto results = EXPECT_JSON_OBJECT_WITH_ARRAY_PROPERTY (run, "results"); - ASSERT_EQ (results->size (), 1); - - // 3.27 "result" object: - auto result = (*results)[0]; - return expect_json_object (SELFTEST_LOCATION, result); -} - -static const json::object * -get_message_from_result (const sarif_result &result) -{ - // 3.27.11: - auto message_obj - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (&result, "message"); - return message_obj; -} - -/* Assuming that a single diagnostic has been emitted to - DC, get a json::object for the messsage object within - the result. */ - -static const json::object * -get_message_from_log (const sarif_log *log) -{ - auto result_obj = get_result_from_log (log); - - // 3.27.11: - auto message_obj - = EXPECT_JSON_OBJECT_WITH_OBJECT_PROPERTY (result_obj, "message"); - return message_obj; -} - -/* Tests of messages with embedded links; see SARIF v2.1.0 3.11.6. */ - -static void -test_message_with_embedded_link (const sarif_generation_options &sarif_gen_opts) -{ - auto_fix_quotes fix_quotes; - { - test_sarif_diagnostic_context dc ("test.c", sarif_gen_opts); - rich_location richloc (line_table, UNKNOWN_LOCATION); - dc.report (DK_ERROR, richloc, nullptr, 0, - "before %{text%} after", - "http://example.com"); - std::unique_ptr<sarif_log> log = dc.flush_to_object (); - - auto message_obj = get_message_from_log (log.get ()); - ASSERT_JSON_STRING_PROPERTY_EQ - (message_obj, "text", - "before [text](http://example.com) after"); - } - - /* Escaping in message text. - This is "EXAMPLE 1" from 3.11.6. */ - { - test_sarif_diagnostic_context dc ("test.c", sarif_gen_opts); - rich_location richloc (line_table, UNKNOWN_LOCATION); - - /* Disable "unquoted sequence of 2 consecutive punctuation - characters `]\' in format" warning. */ -#if __GNUC__ >= 10 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wformat-diag" -#endif - dc.report (DK_ERROR, richloc, nullptr, 0, - "Prohibited term used in %{para[0]\\spans[2]%}.", - "1"); -#if __GNUC__ >= 10 -# pragma GCC diagnostic pop -#endif - - std::unique_ptr<sarif_log> log = dc.flush_to_object (); - - auto message_obj = get_message_from_log (log.get ()); - ASSERT_JSON_STRING_PROPERTY_EQ - (message_obj, "text", - "Prohibited term used in [para\\[0\\]\\\\spans\\[2\\]](1)."); - /* This isn't exactly what EXAMPLE 1 of the spec has; reported as - https://github.com/oasis-tcs/sarif-spec/issues/656 */ - } - - /* Urlifier. */ - { - class test_urlifier : public urlifier - { - public: - char * - get_url_for_quoted_text (const char *p, size_t sz) const final override - { - if (!strncmp (p, "-foption", sz)) - return xstrdup ("http://example.com"); - return nullptr; - } - }; - - test_sarif_diagnostic_context dc ("test.c", sarif_gen_opts); - dc.push_owned_urlifier (std::make_unique<test_urlifier> ()); - rich_location richloc (line_table, UNKNOWN_LOCATION); - dc.report (DK_ERROR, richloc, nullptr, 0, - "foo %<-foption%> %<unrecognized%> bar"); - std::unique_ptr<sarif_log> log = dc.flush_to_object (); - - auto message_obj = get_message_from_log (log.get ()); - ASSERT_JSON_STRING_PROPERTY_EQ - (message_obj, "text", - "foo `[-foption](http://example.com)' `unrecognized' bar"); - } -} - -/* Verify that braces in messages get escaped, as per - 3.11.5 ("Messages with placeholders"). */ - -static void -test_message_with_braces (const sarif_generation_options &sarif_gen_opts) -{ - auto_fix_quotes fix_quotes; - { - test_sarif_diagnostic_context dc ("test.c", sarif_gen_opts); - rich_location richloc (line_table, UNKNOWN_LOCATION); - dc.report (DK_ERROR, richloc, nullptr, 0, - "open brace: %qs close brace: %qs", - "{", "}"); - std::unique_ptr<sarif_log> log = dc.flush_to_object (); - - auto message_obj = get_message_from_log (log.get ()); - ASSERT_JSON_STRING_PROPERTY_EQ - (message_obj, "text", - "open brace: `{{' close brace: `}}'"); - } -} - -static void -test_buffering (const sarif_generation_options &sarif_gen_opts) -{ - test_sarif_diagnostic_context dc ("test.c", sarif_gen_opts); - - diagnostic_buffer buf_a (dc); - diagnostic_buffer buf_b (dc); - - rich_location rich_loc (line_table, UNKNOWN_LOCATION); - - ASSERT_EQ (dc.diagnostic_count (DK_ERROR), 0); - ASSERT_EQ (buf_a.diagnostic_count (DK_ERROR), 0); - ASSERT_EQ (buf_b.diagnostic_count (DK_ERROR), 0); - ASSERT_EQ (dc.num_results (), 0); - ASSERT_TRUE (buf_a.empty_p ()); - ASSERT_TRUE (buf_b.empty_p ()); - - /* Unbuffered diagnostic. */ - { - dc.report (DK_ERROR, rich_loc, nullptr, 0, - "message 1"); - - ASSERT_EQ (dc.diagnostic_count (DK_ERROR), 1); - ASSERT_EQ (buf_a.diagnostic_count (DK_ERROR), 0); - ASSERT_EQ (buf_b.diagnostic_count (DK_ERROR), 0); - ASSERT_EQ (dc.num_results (), 1); - sarif_result &result_obj = dc.get_result (0); - auto message_obj = get_message_from_result (result_obj); - ASSERT_JSON_STRING_PROPERTY_EQ (message_obj, "text", - "message 1"); - ASSERT_TRUE (buf_a.empty_p ()); - ASSERT_TRUE (buf_b.empty_p ()); - } - - /* Buffer diagnostic into buffer A. */ - { - dc.set_diagnostic_buffer (&buf_a); - dc.report (DK_ERROR, rich_loc, nullptr, 0, - "message in buffer a"); - ASSERT_EQ (dc.diagnostic_count (DK_ERROR), 1); - ASSERT_EQ (buf_a.diagnostic_count (DK_ERROR), 1); - ASSERT_EQ (buf_b.diagnostic_count (DK_ERROR), 0); - ASSERT_EQ (dc.num_results (), 1); - ASSERT_FALSE (buf_a.empty_p ()); - ASSERT_TRUE (buf_b.empty_p ()); - } - - /* Buffer diagnostic into buffer B. */ - { - dc.set_diagnostic_buffer (&buf_b); - dc.report (DK_ERROR, rich_loc, nullptr, 0, - "message in buffer b"); - ASSERT_EQ (dc.diagnostic_count (DK_ERROR), 1); - ASSERT_EQ (buf_a.diagnostic_count (DK_ERROR), 1); - ASSERT_EQ (buf_b.diagnostic_count (DK_ERROR), 1); - ASSERT_EQ (dc.num_results (), 1); - ASSERT_FALSE (buf_a.empty_p ()); - ASSERT_FALSE (buf_b.empty_p ()); - } - - /* Flush buffer B to dc. */ - { - dc.flush_diagnostic_buffer (buf_b); - ASSERT_EQ (dc.diagnostic_count (DK_ERROR), 2); - ASSERT_EQ (buf_a.diagnostic_count (DK_ERROR), 1); - ASSERT_EQ (buf_b.diagnostic_count (DK_ERROR), 0); - ASSERT_EQ (dc.num_results (), 2); - sarif_result &result_1_obj = dc.get_result (1); - auto message_1_obj = get_message_from_result (result_1_obj); - ASSERT_JSON_STRING_PROPERTY_EQ (message_1_obj, "text", - "message in buffer b"); - ASSERT_FALSE (buf_a.empty_p ()); - ASSERT_TRUE (buf_b.empty_p ()); - } - - /* Clear buffer A. */ - { - dc.clear_diagnostic_buffer (buf_a); - ASSERT_EQ (dc.diagnostic_count (DK_ERROR), 2); - ASSERT_EQ (buf_a.diagnostic_count (DK_ERROR), 0); - ASSERT_EQ (buf_b.diagnostic_count (DK_ERROR), 0); - ASSERT_EQ (dc.num_results (), 2); - ASSERT_TRUE (buf_a.empty_p ()); - ASSERT_TRUE (buf_b.empty_p ()); - } -} - -template <class ...ArgTypes> -static void -for_each_sarif_gen_option (void (*callback) (const sarif_generation_options &, - ArgTypes ...), - ArgTypes ...args) -{ - sarif_generation_options sarif_gen_opts; - for (int version_idx = 0; - version_idx < (int)sarif_version::num_versions; - ++version_idx) - { - sarif_gen_opts.m_version = static_cast<enum sarif_version> (version_idx); - - callback (sarif_gen_opts, args...); - } -} - -static void -run_line_table_case_tests_per_version (const line_table_case &case_) -{ - for_each_sarif_gen_option<const line_table_case &> - (test_make_location_object, case_); - - for_each_sarif_gen_option<const line_table_case &> - (test_simple_log_2, case_); -} - -/* Run all of the selftests within this file. */ - -void -diagnostic_format_sarif_cc_tests () -{ - test_sarif_array_of_unique_1 (); - test_sarif_array_of_unique_2 (); - - for_each_sarif_gen_option (test_simple_log); - for_each_sarif_gen_option (test_message_with_embedded_link); - for_each_sarif_gen_option (test_message_with_braces); - for_each_sarif_gen_option (test_buffering); - - /* Run tests per (SARIF gen-option, line-table-case) pair. */ - for_each_line_table_case (run_line_table_case_tests_per_version); -} - -} // namespace selftest - -#endif /* CHECKING_P */ |