diff options
65 files changed, 2333 insertions, 738 deletions
diff --git a/gcc/c/c-objc-common.cc b/gcc/c/c-objc-common.cc index fde9ae6..9d39fcd 100644 --- a/gcc/c/c-objc-common.cc +++ b/gcc/c/c-objc-common.cc @@ -34,7 +34,7 @@ along with GCC; see the file COPYING3. If not see #include "dwarf2.h" static bool c_tree_printer (pretty_printer *, text_info *, const char *, - int, bool, bool, bool, bool *, const char **); + int, bool, bool, bool, bool *, pp_token_list &); /* Info for C language features which can be queried through __has_{feature,extension}. */ @@ -318,7 +318,7 @@ pp_markup::element_quoted_type::print_type (pp_markup::context &ctxt) static bool c_tree_printer (pretty_printer *pp, text_info *text, const char *spec, int precision, bool wide, bool set_locus, bool hash, - bool *quoted, const char **) + bool *quoted, pp_token_list &) { tree t = NULL_TREE; // FIXME: the next cast should be a dynamic_cast, when it is permitted. diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index 033ea61..27e24ba 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -22,6 +22,7 @@ #define INCLUDE_STRING #define INCLUDE_ALGORITHM +#define INCLUDE_MEMORY #define INCLUDE_VECTOR #include "config.h" #include "system.h" diff --git a/gcc/config/arm/arm.cc b/gcc/config/arm/arm.cc index d54564a..1748544 100644 --- a/gcc/config/arm/arm.cc +++ b/gcc/config/arm/arm.cc @@ -23,6 +23,7 @@ #define IN_TARGET_CODE 1 #include "config.h" +#define INCLUDE_MEMORY #define INCLUDE_STRING #include "system.h" #include "coretypes.h" diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc index 224a78c..a1f65d4 100644 --- a/gcc/config/i386/i386.cc +++ b/gcc/config/i386/i386.cc @@ -17,6 +17,7 @@ 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/>. */ +#define INCLUDE_MEMORY #define INCLUDE_STRING #define IN_TARGET_CODE 1 diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc index c7a0210..f956ee4 100644 --- a/gcc/config/loongarch/loongarch.cc +++ b/gcc/config/loongarch/loongarch.cc @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #define IN_TARGET_CODE 1 +#define INCLUDE_MEMORY #include "config.h" #include "system.h" #include "coretypes.h" diff --git a/gcc/config/riscv/riscv-vector-costs.cc b/gcc/config/riscv/riscv-vector-costs.cc index a80e167..25570bd 100644 --- a/gcc/config/riscv/riscv-vector-costs.cc +++ b/gcc/config/riscv/riscv-vector-costs.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see #define IN_TARGET_CODE 1 +#define INCLUDE_MEMORY #define INCLUDE_STRING #include "config.h" #include "system.h" diff --git a/gcc/config/riscv/riscv.cc b/gcc/config/riscv/riscv.cc index 3f5dfb8..496dd17 100644 --- a/gcc/config/riscv/riscv.cc +++ b/gcc/config/riscv/riscv.cc @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #define IN_TARGET_CODE 1 +#define INCLUDE_MEMORY #define INCLUDE_STRING #include "config.h" #include "system.h" diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc index 94c0db4..08579bc 100644 --- a/gcc/config/rs6000/rs6000.cc +++ b/gcc/config/rs6000/rs6000.cc @@ -22,6 +22,7 @@ #define IN_TARGET_CODE 1 #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index 879e5a1..420fad2 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -38,6 +38,8 @@ along with GCC; see the file COPYING3. If not see #include "c-family/c-type-mismatch.h" #include "cp-name-hint.h" #include "attribs.h" +#include "pretty-print-format-impl.h" +#include "make-unique.h" #define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',') #define pp_separate_with_semicolon(PP) pp_cxx_separate_with (PP, ';') @@ -109,7 +111,7 @@ static void cp_print_error_function (diagnostic_context *, const diagnostic_info *); static bool cp_printer (pretty_printer *, text_info *, const char *, - int, bool, bool, bool, bool *, const char **); + int, bool, bool, bool, bool *, pp_token_list &); /* Color names for highlighting "%qH" vs "%qI" values, and ranges corresponding to them. */ @@ -123,22 +125,50 @@ class deferred_printed_type { public: deferred_printed_type () - : m_tree (NULL_TREE), m_buffer_ptr (NULL), m_verbose (false), m_quote (false) + : m_tree (NULL_TREE), + m_printed_text (), + m_token_list (nullptr), + m_verbose (false), m_quote (false) {} - deferred_printed_type (tree type, const char **buffer_ptr, bool verbose, + deferred_printed_type (tree type, + pp_token_list &token_list, + bool verbose, bool quote) - : m_tree (type), m_buffer_ptr (buffer_ptr), m_verbose (verbose), + : m_tree (type), + m_printed_text (), + m_token_list (&token_list), + m_verbose (verbose), m_quote (quote) { gcc_assert (type); - gcc_assert (buffer_ptr); + } + + void set_text_for_token_list (const char *text, bool quote) + { + /* Replace the contents of m_token_list with a text token for TEXT, + possibly wrapped by BEGIN_QUOTE/END_QUOTE (if QUOTE is true). + This allows us to ignore any {BEGIN,END}_QUOTE tokens added + by %qH and %qI, and instead use the quoting from type_to_string, + and its logic for "aka". */ + while (m_token_list->m_first) + m_token_list->pop_front (); + + if (quote) + m_token_list->push_back<pp_token_begin_quote> (); + + // TEXT is gc-allocated, so we can borrow it + m_token_list->push_back_text (label_text::borrow (text)); + + if (quote) + m_token_list->push_back<pp_token_end_quote> (); } /* The tree is not GTY-marked: they are only non-NULL within a call to pp_format. */ tree m_tree; - const char **m_buffer_ptr; + label_text m_printed_text; + pp_token_list *m_token_list; bool m_verbose; bool m_quote; }; @@ -4401,26 +4431,7 @@ append_formatted_chunk (pretty_printer *pp, const char *content) { output_buffer *buffer = pp_buffer (pp); chunk_info *chunk_array = buffer->cur_chunk_array; - chunk_array->append_formatted_chunk (content); -} - -/* Create a copy of CONTENT, with quotes added, and, - potentially, with colorization. - No escaped is performed on CONTENT. - The result is in a GC-allocated buffer. */ - -static const char * -add_quotes (const char *content, bool show_color) -{ - pretty_printer tmp_pp; - pp_show_color (&tmp_pp) = show_color; - - /* We have to use "%<%s%>" rather than "%qs" here in order to avoid - quoting colorization bytes within the results and using either - pp_quote or pp_begin_quote doesn't work the same. */ - pp_printf (&tmp_pp, "%<%s%>", content); - - return pp_ggc_formatted_text (&tmp_pp); + chunk_array->append_formatted_chunk (buffer->chunk_obstack, content); } #if __GNUC__ >= 10 @@ -4428,8 +4439,8 @@ add_quotes (const char *content, bool show_color) #endif /* If we had %H and %I, and hence deferred printing them, - print them now, storing the result into the chunk_info - for pp_format. Quote them if 'q' was provided. + print them now, storing the result into custom_token_value + for the custom pp_token. Quote them if 'q' was provided. Also print the difference in tree form, adding it as an additional chunk. */ @@ -4447,13 +4458,13 @@ cxx_format_postprocessor::handle (pretty_printer *pp) = show_highlight_colors ? highlight_colors::percent_i : nullptr; /* Avoid reentrancy issues by working with a copy of m_type_a and m_type_b, resetting them now. */ - deferred_printed_type type_a = m_type_a; - deferred_printed_type type_b = m_type_b; + deferred_printed_type type_a = std::move (m_type_a); + deferred_printed_type type_b = std::move (m_type_b); m_type_a = deferred_printed_type (); m_type_b = deferred_printed_type (); - gcc_assert (type_a.m_buffer_ptr); - gcc_assert (type_b.m_buffer_ptr); + gcc_assert (type_a.m_token_list); + gcc_assert (type_b.m_token_list); bool show_color = pp_show_color (pp); @@ -4494,13 +4505,8 @@ cxx_format_postprocessor::handle (pretty_printer *pp) percent_i); } - if (type_a.m_quote) - type_a_text = add_quotes (type_a_text, show_color); - *type_a.m_buffer_ptr = type_a_text; - - if (type_b.m_quote) - type_b_text = add_quotes (type_b_text, show_color); - *type_b.m_buffer_ptr = type_b_text; + type_a.set_text_for_token_list (type_a_text, type_a.m_quote); + type_b.set_text_for_token_list (type_b_text, type_b.m_quote); } } @@ -4525,9 +4531,12 @@ cxx_format_postprocessor::handle (pretty_printer *pp) pretty_printer's m_format_postprocessor hook. This is called in phase 2 of pp_format, when it is accumulating - a series of formatted chunks. We stash the location of the chunk - we're meant to have written to, so that we can write to it in the - m_format_postprocessor hook. + a series of pp_token lists. Since we have to interact with the + fiddly quoting logic for "aka", we store the pp_token_list * + and in the m_format_postprocessor hook we generate text for the type + (possibly with quotes and colors), then replace all tokens in that token list + (such as [BEGIN_QUOTE, END_QUOTE]) with a text token containing the + freshly generated text. We also need to stash whether a 'q' prefix was provided (the QUOTE param) so that we can add the quotes when writing out the delayed @@ -4535,12 +4544,13 @@ cxx_format_postprocessor::handle (pretty_printer *pp) static void defer_phase_2_of_type_diff (deferred_printed_type *deferred, - tree type, const char **buffer_ptr, + tree type, + pp_token_list &formatted_token_list, bool verbose, bool quote) { gcc_assert (deferred->m_tree == NULL_TREE); - gcc_assert (deferred->m_buffer_ptr == NULL); - *deferred = deferred_printed_type (type, buffer_ptr, verbose, quote); + *deferred = deferred_printed_type (type, formatted_token_list, + verbose, quote); } /* Implementation of pp_markup::element_quoted_type::print_type @@ -4577,7 +4587,7 @@ pp_markup::element_quoted_type::print_type (pp_markup::context &ctxt) static bool cp_printer (pretty_printer *pp, text_info *text, const char *spec, int precision, bool wide, bool set_locus, bool verbose, - bool *quoted, const char **buffer_ptr) + bool *quoted, pp_token_list &formatted_token_list) { gcc_assert (pp_format_postprocessor (pp)); cxx_format_postprocessor *postprocessor @@ -4617,11 +4627,11 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec, case 'F': result = fndecl_to_string (next_tree, verbose); break; case 'H': defer_phase_2_of_type_diff (&postprocessor->m_type_a, next_tree, - buffer_ptr, verbose, *quoted); + formatted_token_list, verbose, *quoted); return true; case 'I': defer_phase_2_of_type_diff (&postprocessor->m_type_b, next_tree, - buffer_ptr, verbose, *quoted); + formatted_token_list, verbose, *quoted); return true; case 'L': result = language_to_string (next_lang); break; case 'O': result = op_to_string (false, next_tcode); break; diff --git a/gcc/diagnostic-event-id.h b/gcc/diagnostic-event-id.h index 78c2ccb..8237ba3 100644 --- a/gcc/diagnostic-event-id.h +++ b/gcc/diagnostic-event-id.h @@ -41,6 +41,12 @@ class diagnostic_event_id_t bool known_p () const { return m_index != UNKNOWN_EVENT_IDX; } + int zero_based () const + { + gcc_assert (known_p ()); + return m_index; + } + int one_based () const { gcc_assert (known_p ()); diff --git a/gcc/diagnostic-format-sarif.cc b/gcc/diagnostic-format-sarif.cc index 59d9cd7..9d9e7ae 100644 --- a/gcc/diagnostic-format-sarif.cc +++ b/gcc/diagnostic-format-sarif.cc @@ -45,6 +45,8 @@ along with GCC; see the file COPYING3. If not see #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" /* Forward decls. */ class sarif_builder; @@ -385,7 +387,12 @@ private: class sarif_result : public sarif_location_manager { public: - sarif_result () : m_related_locations_arr (nullptr) {} + sarif_result (unsigned idx_within_parent) + : m_related_locations_arr (nullptr), + m_idx_within_parent (idx_within_parent) + {} + + unsigned get_index_within_parent () const { return m_idx_within_parent; } void on_nested_diagnostic (diagnostic_context &context, @@ -402,6 +409,7 @@ public: private: json::array *m_related_locations_arr; // borrowed + const unsigned m_idx_within_parent; }; /* Subclass of sarif_object for SARIF "location" objects @@ -460,7 +468,39 @@ private: /* Subclass of sarif_object for SARIF "codeFlow" objects (SARIF v2.1.0 section 3.36). */ -class sarif_code_flow : public sarif_object {}; +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). */ @@ -468,20 +508,41 @@ class sarif_code_flow : public sarif_object {}; class sarif_thread_flow : public sarif_object { public: - sarif_thread_flow (const diagnostic_thread &thread); + sarif_thread_flow (sarif_code_flow &parent, + const diagnostic_thread &thread, + unsigned idx_within_parent); - void - add_location - (std::unique_ptr<sarif_thread_flow_location> thread_flow_loc_obj); + 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 {}; +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). */ @@ -632,11 +693,33 @@ public: 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; + } + + token_printer &get_token_printer () { return m_token_printer; } + 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 (diagnostic_context &context, const diagnostic_info &diagnostic, - diagnostic_t orig_diag_kind); + diagnostic_t orig_diag_kind, + unsigned idx_within_parent); void add_any_include_chain (sarif_location_manager &loc_mgr, sarif_location &location_obj, @@ -650,11 +733,13 @@ private: 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); - std::unique_ptr<sarif_thread_flow_location> - make_thread_flow_location_object (sarif_result &result, - const diagnostic_event &event, - int path_event_idx); + 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> @@ -725,6 +810,7 @@ private: diagnostic_context &m_context; const line_maps *m_line_maps; + sarif_token_printer m_token_printer; /* The JSON object for the invocation object. */ std::unique_ptr<sarif_invocation> m_invocation_obj; @@ -751,6 +837,9 @@ private: int m_tabstop; bool m_formatted; + + unsigned m_next_result_idx; + sarif_code_flow *m_current_code_flow; }; /* class sarif_object : public json::object. */ @@ -1294,9 +1383,67 @@ lazily_add_kind (enum 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 = ::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 + = ::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 (const diagnostic_thread &thread) +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)); @@ -1310,11 +1457,18 @@ sarif_thread_flow::sarif_thread_flow (const diagnostic_thread &thread) set ("locations", m_locations_arr); } -void -sarif_thread_flow:: -add_location (std::unique_ptr<sarif_thread_flow_location> thread_flow_loc_obj) +/* 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 () { - m_locations_arr->append (std::move (thread_flow_loc_obj)); + 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. */ @@ -1327,6 +1481,7 @@ sarif_builder::sarif_builder (diagnostic_context &context, bool formatted) : m_context (context), m_line_maps (line_maps), + m_token_printer (*this), m_invocation_obj (::make_unique<sarif_invocation> (*this, context.get_original_argv ())), @@ -1336,7 +1491,9 @@ sarif_builder::sarif_builder (diagnostic_context &context, m_rule_id_set (), m_rules_arr (new json::array ()), m_tabstop (context.m_tabstop), - m_formatted (formatted) + m_formatted (formatted), + m_next_result_idx (0), + m_current_code_flow (nullptr) { gcc_assert (m_line_maps); @@ -1376,7 +1533,8 @@ sarif_builder::on_report_diagnostic (diagnostic_context &context, { /* Top-level diagnostic. */ m_cur_group_result - = make_result_object (context, diagnostic, orig_diag_kind); + = make_result_object (context, diagnostic, orig_diag_kind, + m_next_result_idx++); } } @@ -1476,9 +1634,10 @@ make_rule_id_for_diagnostic_kind (diagnostic_t diag_kind) std::unique_ptr<sarif_result> sarif_builder::make_result_object (diagnostic_context &context, const diagnostic_info &diagnostic, - diagnostic_t orig_diag_kind) + diagnostic_t orig_diag_kind, + unsigned idx_within_parent) { - auto result_obj = ::make_unique<sarif_result> (); + auto result_obj = ::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. */ @@ -1552,8 +1711,10 @@ sarif_builder::make_result_object (diagnostic_context &context, if (const diagnostic_path *path = diagnostic.richloc->get_path ()) { auto code_flows_arr = ::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)); } @@ -2272,83 +2433,115 @@ make_sarif_logical_location_object (const logical_location &logical_loc) return logical_loc_obj; } +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 = ::make_unique <sarif_code_flow> (); + auto code_flow_obj + = ::make_unique <sarif_code_flow> (result, idx_within_parent); - /* "threadFlows" property (SARIF v2.1.0 section 3.36.3). */ - auto thread_flows_arr = ::make_unique<json::array> (); - - /* Walk the events, consolidating into per-thread threadFlow objects, - using the index with PATH as the overall executionOrder. */ - hash_map<int_hash<diagnostic_thread_id_t, -1, -2>, - sarif_thread_flow *> thread_id_map; // borrowed + /* 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; - if (sarif_thread_flow **slot = thread_id_map.get (thread_id)) - thread_flow_obj = *slot; - else - { - const diagnostic_thread &thread = path.get_thread (thread_id); - thread_flow_obj = new sarif_thread_flow (thread); - thread_id_map.put (thread_id, thread_flow_obj); // borrowed - thread_flows_arr->append (thread_flow_obj); - } + 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 (); + } - /* Add event to thread's threadFlow object. */ - std::unique_ptr<sarif_thread_flow_location> thread_flow_loc_obj - = make_thread_flow_location_object (result, event, i); - thread_flow_obj->add_location (std::move (thread_flow_loc_obj)); + /* 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); } - code_flow_obj->set<json::array> ("threadFlows", std::move (thread_flows_arr)); + m_current_code_flow = nullptr; return code_flow_obj; } -/* Make a "threadFlowLocation" object (SARIF v2.1.0 section 3.38) for EVENT. */ +/* Populate TFL_OBJ, a "threadFlowLocation" object (SARIF v2.1.0 section 3.38) + based on EVENT. */ -std::unique_ptr<sarif_thread_flow_location> -sarif_builder::make_thread_flow_location_object (sarif_result &result, - const diagnostic_event &ev, - int path_event_idx) +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) { - auto thread_flow_loc_obj = ::make_unique<sarif_thread_flow_location> (); - /* Give diagnostic_event subclasses a chance to add custom properties via a property bag. */ - ev.maybe_add_sarif_properties (*thread_flow_loc_obj); + ev.maybe_add_sarif_properties (tfl_obj); /* "location" property (SARIF v2.1.0 section 3.38.3). */ - thread_flow_loc_obj->set<sarif_location> + 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)) - thread_flow_loc_obj->set<json::array> ("kinds", std::move (kinds_arr)); + tfl_obj.set<json::array> ("kinds", std::move (kinds_arr)); /* "nestingLevel" property (SARIF v2.1.0 section 3.38.10). */ - thread_flow_loc_obj->set_integer ("nestingLevel", ev.get_stack_depth ()); + 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 %@. */ - thread_flow_loc_obj->set_integer ("executionOrder", path_event_idx + 1); + 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). */ - - return thread_flow_loc_obj; } /* If M has any known meaning, make a json array suitable for the "kinds" @@ -2933,6 +3126,8 @@ public: m_builder.emit_diagram (m_context, diagram); } + sarif_builder &get_builder () { return m_builder; } + protected: sarif_output_format (diagnostic_context &context, const line_maps *line_maps, @@ -3008,11 +3203,128 @@ private: char *m_base_file_name; }; +/* 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) +diagnostic_output_format_init_sarif (diagnostic_context &context, + std::unique_ptr<sarif_output_format> fmt) { /* Suppress normal textual path output. */ context.set_path_format (DPF_NONE); @@ -3023,6 +3335,10 @@ diagnostic_output_format_init_sarif (diagnostic_context &context) /* Don't colorize the text. */ pp_show_color (context.printer) = false; context.set_show_highlight_colors (false); + + context.printer->set_token_printer + (&fmt->get_builder ().get_token_printer ()); + context.set_output_format (fmt.release ()); } /* Populate CONTEXT in preparation for SARIF output to stderr. */ @@ -3034,13 +3350,13 @@ diagnostic_output_format_init_sarif_stderr (diagnostic_context &context, bool formatted) { gcc_assert (line_maps); - diagnostic_output_format_init_sarif (context); - context.set_output_format - (new sarif_stream_output_format (context, - line_maps, - main_input_filename_, - formatted, - stderr)); + diagnostic_output_format_init_sarif + (context, + ::make_unique<sarif_stream_output_format> (context, + line_maps, + main_input_filename_, + formatted, + stderr)); } /* Populate CONTEXT in preparation for SARIF output to a file named @@ -3054,13 +3370,13 @@ diagnostic_output_format_init_sarif_file (diagnostic_context &context, const char *base_file_name) { gcc_assert (line_maps); - diagnostic_output_format_init_sarif (context); - context.set_output_format - (new sarif_file_output_format (context, - line_maps, - main_input_filename_, - formatted, - base_file_name)); + diagnostic_output_format_init_sarif + (context, + ::make_unique<sarif_file_output_format> (context, + line_maps, + main_input_filename_, + formatted, + base_file_name)); } /* Populate CONTEXT in preparation for SARIF output to STREAM. */ @@ -3073,13 +3389,13 @@ diagnostic_output_format_init_sarif_stream (diagnostic_context &context, FILE *stream) { gcc_assert (line_maps); - diagnostic_output_format_init_sarif (context); - context.set_output_format - (new sarif_stream_output_format (context, - line_maps, - main_input_filename_, - formatted, - stream)); + diagnostic_output_format_init_sarif + (context, + ::make_unique<sarif_stream_output_format> (context, + line_maps, + main_input_filename_, + formatted, + stream)); } #if CHECKING_P @@ -3095,13 +3411,12 @@ class test_sarif_diagnostic_context : public test_diagnostic_context public: test_sarif_diagnostic_context (const char *main_input_filename) { - diagnostic_output_format_init_sarif (*this); - - m_format = new buffered_output_format (*this, - line_table, - main_input_filename, - true); - set_output_format (m_format); // give ownership; + auto format = ::make_unique<buffered_output_format> (*this, + line_table, + main_input_filename, + true); + m_format = format.get (); // borrowed + diagnostic_output_format_init_sarif (*this, std::move (format)); } std::unique_ptr<sarif_log> flush_to_object () @@ -3175,7 +3490,7 @@ test_make_location_object (const line_table_case &case_) richloc.add_range (field, SHOW_RANGE_WITHOUT_CARET, &label2); richloc.set_escape_on_output (true); - sarif_result result; + sarif_result result (0); std::unique_ptr<sarif_location> location_obj = builder.make_location_object @@ -3471,6 +3786,119 @@ test_simple_log_2 (const line_table_case &case_) } } +/* 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); +} + +/* 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 () +{ + auto_fix_quotes fix_quotes; + { + test_sarif_diagnostic_context dc ("test.c"); + 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"); + 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"); + dc.set_urlifier (new 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"); + } +} + /* Run all of the selftests within this file. */ void @@ -3479,6 +3907,7 @@ diagnostic_format_sarif_cc_tests () for_each_line_table_case (test_make_location_object); test_simple_log (); for_each_line_table_case (test_simple_log_2); + test_message_with_embedded_link (); } } // namespace selftest diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc index 381a050..a80e16b 100644 --- a/gcc/diagnostic.cc +++ b/gcc/diagnostic.cc @@ -1407,7 +1407,7 @@ diagnostic_context::report_diagnostic (diagnostic_info *diagnostic) m_output_format->on_begin_group (); m_diagnostic_groups.m_emission_count++; - pp_format (this->printer, &diagnostic->message, m_urlifier); + pp_format (this->printer, &diagnostic->message); /* Call vfunc in the output format. This is responsible for phase 3 of formatting, and for printing the result. */ m_output_format->on_report_diagnostic (*diagnostic, orig_diag_kind); diff --git a/gcc/dump-context.h b/gcc/dump-context.h index b2aed28..e90c4ee 100644 --- a/gcc/dump-context.h +++ b/gcc/dump-context.h @@ -120,7 +120,7 @@ class dump_context void end_any_optinfo (); void emit_optinfo (const optinfo *info); - void emit_item (optinfo_item *item, dump_flags_t dump_kind); + void emit_item (const optinfo_item &item, dump_flags_t dump_kind); bool apply_dump_filter_p (dump_flags_t dump_kind, dump_flags_t filter) const; @@ -154,47 +154,52 @@ class dump_context }; /* A subclass of pretty_printer for implementing dump_context::dump_printf_va. - In particular, the formatted chunks are captured as optinfo_item instances, - thus retaining metadata about the entities being dumped (e.g. source - locations), rather than just as plain text. */ + In particular, the formatted chunks are captured as optinfo_item instances + as pp_token_custom_data, thus retaining metadata about the entities being + dumped (e.g. source locations), rather than just as plain text. + These custom items are retained through to the end of stage 3 of formatted + printing; the printer uses a custom token_printer subclass to emit them to + the active optinfo (if any). */ class dump_pretty_printer : public pretty_printer { public: dump_pretty_printer (dump_context *context, dump_flags_t dump_kind); - void emit_items (optinfo *dest); + void set_optinfo (optinfo *info) { m_token_printer.m_optinfo = info; } private: - /* Information on an optinfo_item that was generated during phase 2 of - formatting. */ - class stashed_item + struct custom_token_printer : public token_printer { - public: - stashed_item (const char **buffer_ptr_, optinfo_item *item_) - : buffer_ptr (buffer_ptr_), item (item_) {} - const char **buffer_ptr; - optinfo_item *item; + custom_token_printer (dump_pretty_printer &dump_pp) + : m_dump_pp (dump_pp), + m_optinfo (nullptr) + {} + void print_tokens (pretty_printer *pp, + const pp_token_list &tokens) final override; + void emit_any_pending_textual_chunks (); + + dump_pretty_printer &m_dump_pp; + optinfo *m_optinfo; }; static bool format_decoder_cb (pretty_printer *pp, text_info *text, const char *spec, int /*precision*/, bool /*wide*/, bool /*set_locus*/, bool /*verbose*/, bool */*quoted*/, - const char **buffer_ptr); + pp_token_list &formatted_tok_list); bool decode_format (text_info *text, const char *spec, - const char **buffer_ptr); + pp_token_list &formatted_tok_list); - void stash_item (const char **buffer_ptr, optinfo_item *item); + void stash_item (pp_token_list &formatted_tok_list, + std::unique_ptr<optinfo_item> item); - void emit_any_pending_textual_chunks (optinfo *dest); - - void emit_item (optinfo_item *item, optinfo *dest); + void emit_item (std::unique_ptr<optinfo_item> item, optinfo *dest); dump_context *m_context; dump_flags_t m_dump_kind; - auto_vec<stashed_item> m_stashed_items; + custom_token_printer m_token_printer; }; /* An RAII-style class for use in debug dumpers for temporarily using a diff --git a/gcc/dumpfile.cc b/gcc/dumpfile.cc index 6353c08..da36718 100644 --- a/gcc/dumpfile.cc +++ b/gcc/dumpfile.cc @@ -41,6 +41,8 @@ along with GCC; see the file COPYING3. If not see #include "optinfo-emit-json.h" #include "stringpool.h" /* for get_identifier. */ #include "spellcheck.h" +#include "make-unique.h" +#include "pretty-print-format-impl.h" /* If non-NULL, return one past-the-end of the matching SUBPART of the WHOLE string. */ @@ -628,7 +630,7 @@ dump_context::dump_loc_immediate (dump_flags_t dump_kind, /* Make an item for the given dump call, equivalent to print_gimple_stmt. */ -static optinfo_item * +static std::unique_ptr<optinfo_item> make_item_for_dump_gimple_stmt (gimple *stmt, int spc, dump_flags_t dump_flags) { pretty_printer pp; @@ -636,9 +638,10 @@ make_item_for_dump_gimple_stmt (gimple *stmt, int spc, dump_flags_t dump_flags) pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags); pp_newline (&pp); - optinfo_item *item - = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location (stmt), - xstrdup (pp_formatted_text (&pp))); + std::unique_ptr<optinfo_item> item + = make_unique<optinfo_item> (OPTINFO_ITEM_KIND_GIMPLE, + gimple_location (stmt), + xstrdup (pp_formatted_text (&pp))); return item; } @@ -650,17 +653,15 @@ dump_context::dump_gimple_stmt (const dump_metadata_t &metadata, dump_flags_t extra_dump_flags, gimple *gs, int spc) { - optinfo_item *item + auto item = make_item_for_dump_gimple_stmt (gs, spc, dump_flags | extra_dump_flags); - emit_item (item, metadata.get_dump_flags ()); + emit_item (*item.get (), metadata.get_dump_flags ()); if (optinfo_enabled_p ()) { optinfo &info = ensure_pending_optinfo (metadata); - info.add_item (item); + info.add_item (std::move (item)); } - else - delete item; } /* Similar to dump_gimple_stmt, except additionally print source location. */ @@ -677,7 +678,7 @@ dump_context::dump_gimple_stmt_loc (const dump_metadata_t &metadata, /* Make an item for the given dump call, equivalent to print_gimple_expr. */ -static optinfo_item * +static std::unique_ptr<optinfo_item> make_item_for_dump_gimple_expr (gimple *stmt, int spc, dump_flags_t dump_flags) { dump_flags |= TDF_RHS_ONLY; @@ -685,9 +686,10 @@ make_item_for_dump_gimple_expr (gimple *stmt, int spc, dump_flags_t dump_flags) pp_needs_newline (&pp) = true; pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags); - optinfo_item *item - = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location (stmt), - xstrdup (pp_formatted_text (&pp))); + std::unique_ptr<optinfo_item> item + = make_unique<optinfo_item> (OPTINFO_ITEM_KIND_GIMPLE, + gimple_location (stmt), + xstrdup (pp_formatted_text (&pp))); return item; } @@ -700,17 +702,15 @@ dump_context::dump_gimple_expr (const dump_metadata_t &metadata, dump_flags_t extra_dump_flags, gimple *gs, int spc) { - optinfo_item *item + std::unique_ptr<optinfo_item> item = make_item_for_dump_gimple_expr (gs, spc, dump_flags | extra_dump_flags); - emit_item (item, metadata.get_dump_flags ()); + emit_item (*item.get (), metadata.get_dump_flags ()); if (optinfo_enabled_p ()) { optinfo &info = ensure_pending_optinfo (metadata); - info.add_item (item); + info.add_item (std::move (item)); } - else - delete item; } /* Similar to dump_gimple_expr, except additionally print source location. */ @@ -728,7 +728,7 @@ dump_context::dump_gimple_expr_loc (const dump_metadata_t &metadata, /* Make an item for the given dump call, equivalent to print_generic_expr. */ -static optinfo_item * +static std::unique_ptr<optinfo_item> make_item_for_dump_generic_expr (tree node, dump_flags_t dump_flags) { pretty_printer pp; @@ -740,9 +740,9 @@ make_item_for_dump_generic_expr (tree node, dump_flags_t dump_flags) if (EXPR_HAS_LOCATION (node)) loc = EXPR_LOCATION (node); - optinfo_item *item - = new optinfo_item (OPTINFO_ITEM_KIND_TREE, loc, - xstrdup (pp_formatted_text (&pp))); + std::unique_ptr<optinfo_item> item + = make_unique<optinfo_item> (OPTINFO_ITEM_KIND_TREE, loc, + xstrdup (pp_formatted_text (&pp))); return item; } @@ -754,17 +754,15 @@ dump_context::dump_generic_expr (const dump_metadata_t &metadata, dump_flags_t extra_dump_flags, tree t) { - optinfo_item *item + std::unique_ptr<optinfo_item> item = make_item_for_dump_generic_expr (t, dump_flags | extra_dump_flags); - emit_item (item, metadata.get_dump_flags ()); + emit_item (*item.get (), metadata.get_dump_flags ()); if (optinfo_enabled_p ()) { optinfo &info = ensure_pending_optinfo (metadata); - info.add_item (item); + info.add_item (std::move (item)); } - else - delete item; } @@ -783,123 +781,75 @@ dump_context::dump_generic_expr_loc (const dump_metadata_t &metadata, /* Make an item for the given dump call. */ -static optinfo_item * +static std::unique_ptr<optinfo_item> make_item_for_dump_symtab_node (symtab_node *node) { location_t loc = DECL_SOURCE_LOCATION (node->decl); - optinfo_item *item - = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc, - xstrdup (node->dump_name ())); + std::unique_ptr<optinfo_item> item + = make_unique<optinfo_item> (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc, + xstrdup (node->dump_name ())); return item; } -/* dump_pretty_printer's ctor. */ - -dump_pretty_printer::dump_pretty_printer (dump_context *context, - dump_flags_t dump_kind) -: pretty_printer (), m_context (context), m_dump_kind (dump_kind), - m_stashed_items () +struct wrapped_optinfo_item : public pp_token_custom_data::value { - pp_format_decoder (this) = format_decoder_cb; -} - -/* Phase 3 of formatting; compare with pp_output_formatted_text. - - Emit optinfo_item instances for the various formatted chunks from phases - 1 and 2 (i.e. pp_format). - - Some chunks may already have had their items built (during decode_format). - These chunks have been stashed into m_stashed_items; we emit them here. - - For all other purely textual chunks, they are printed into - buffer->formatted_obstack, and then emitted as a textual optinfo_item. - This consolidates multiple adjacent text chunks into a single text - optinfo_item. */ - -void -dump_pretty_printer::emit_items (optinfo *dest) -{ - output_buffer *buffer = pp_buffer (this); - chunk_info *chunk_array = buffer->cur_chunk_array; - const char * const *args = chunk_array->get_args (); - - gcc_assert (buffer->obstack == &buffer->formatted_obstack); - gcc_assert (buffer->line_length == 0); - - unsigned stashed_item_idx = 0; - for (unsigned chunk = 0; args[chunk]; chunk++) - { - if (stashed_item_idx < m_stashed_items.length () - && args[chunk] == *m_stashed_items[stashed_item_idx].buffer_ptr) - { - emit_any_pending_textual_chunks (dest); - /* This chunk has a stashed item: use it. */ - emit_item (m_stashed_items[stashed_item_idx++].item, dest); - } - else - /* This chunk is purely textual. Print it (to - buffer->formatted_obstack), so that we can consolidate adjacent - chunks into one textual optinfo_item. */ - pp_string (this, args[chunk]); - } + wrapped_optinfo_item (std::unique_ptr<optinfo_item> item) + : m_optinfo_item (std::move (item)) + { + gcc_assert (m_optinfo_item.get ()); + } - emit_any_pending_textual_chunks (dest); + void dump (FILE *out) const final override + { + fprintf (out, "OPTINFO(\"%s\")", m_optinfo_item->get_text ()); + } - /* Ensure that we consumed all of stashed_items. */ - gcc_assert (stashed_item_idx == m_stashed_items.length ()); + bool as_standard_tokens (pp_token_list &) final override + { + /* Keep as a custom token. */ + return false; + } - chunk_array->pop_from_output_buffer (*buffer); -} + std::unique_ptr<optinfo_item> m_optinfo_item; +}; -/* Subroutine of dump_pretty_printer::emit_items - for consolidating multiple adjacent pure-text chunks into single - optinfo_items (in phase 3). */ +/* dump_pretty_printer's ctor. */ -void -dump_pretty_printer::emit_any_pending_textual_chunks (optinfo *dest) +dump_pretty_printer::dump_pretty_printer (dump_context *context, + dump_flags_t dump_kind) +: pretty_printer (), + m_context (context), + m_dump_kind (dump_kind), + m_token_printer (*this) { - output_buffer *const buffer = pp_buffer (this); - gcc_assert (buffer->obstack == &buffer->formatted_obstack); - - /* Don't emit an item if the pending text is empty. */ - if (output_buffer_last_position_in_text (buffer) == NULL) - return; - - char *formatted_text = xstrdup (pp_formatted_text (this)); - optinfo_item *item - = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION, - formatted_text); - emit_item (item, dest); - - /* Clear the pending text by unwinding formatted_text back to the start - of the buffer (without deallocating). */ - obstack_free (&buffer->formatted_obstack, - buffer->formatted_obstack.object_base); + pp_format_decoder (this) = format_decoder_cb; + set_token_printer (&m_token_printer); } /* Emit ITEM and take ownership of it. If DEST is non-NULL, add ITEM to DEST; otherwise delete ITEM. */ void -dump_pretty_printer::emit_item (optinfo_item *item, optinfo *dest) +dump_pretty_printer::emit_item (std::unique_ptr<optinfo_item> item, + optinfo *dest) { - m_context->emit_item (item, m_dump_kind); + m_context->emit_item (*item.get (), m_dump_kind); if (dest) - dest->add_item (item); - else - delete item; + dest->add_item (std::move (item)); } -/* Record that ITEM (generated in phase 2 of formatting) is to be used for - the chunk at BUFFER_PTR in phase 3 (by emit_items). */ +/* Append a custom pp_token for ITEM (generated in phase 2 of formatting) + into FORMATTTED_TOK_LIST, so that it can be emitted in phase 2. */ void -dump_pretty_printer::stash_item (const char **buffer_ptr, optinfo_item *item) +dump_pretty_printer::stash_item (pp_token_list &formatted_tok_list, + std::unique_ptr<optinfo_item> item) { - gcc_assert (buffer_ptr); - gcc_assert (item); + gcc_assert (item.get ()); - m_stashed_items.safe_push (stashed_item (buffer_ptr, item)); + auto custom_data + = ::make_unique<wrapped_optinfo_item> (std::move (item)); + formatted_tok_list.push_back<pp_token_custom_data> (std::move (custom_data)); } /* pp_format_decoder callback for dump_pretty_printer, and thus for @@ -912,10 +862,10 @@ dump_pretty_printer::format_decoder_cb (pretty_printer *pp, text_info *text, const char *spec, int /*precision*/, bool /*wide*/, bool /*set_locus*/, bool /*verbose*/, bool */*quoted*/, - const char **buffer_ptr) + pp_token_list &formatted_tok_list) { dump_pretty_printer *opp = static_cast <dump_pretty_printer *> (pp); - return opp->decode_format (text, spec, buffer_ptr); + return opp->decode_format (text, spec, formatted_tok_list); } /* Format decoder for dump_pretty_printer, and thus for dump_printf and @@ -942,7 +892,7 @@ dump_pretty_printer::format_decoder_cb (pretty_printer *pp, text_info *text, bool dump_pretty_printer::decode_format (text_info *text, const char *spec, - const char **buffer_ptr) + pp_token_list &formatted_tok_list) { /* Various format codes that imply making an optinfo_item and stashed it for later use (to capture metadata, rather than plain text). */ @@ -953,8 +903,8 @@ dump_pretty_printer::decode_format (text_info *text, const char *spec, cgraph_node *node = va_arg (*text->m_args_ptr, cgraph_node *); /* Make an item for the node, and stash it. */ - optinfo_item *item = make_item_for_dump_symtab_node (node); - stash_item (buffer_ptr, item); + auto item = make_item_for_dump_symtab_node (node); + stash_item (formatted_tok_list, std::move (item)); return true; } @@ -963,8 +913,8 @@ dump_pretty_printer::decode_format (text_info *text, const char *spec, gimple *stmt = va_arg (*text->m_args_ptr, gimple *); /* Make an item for the stmt, and stash it. */ - optinfo_item *item = make_item_for_dump_gimple_expr (stmt, 0, TDF_SLIM); - stash_item (buffer_ptr, item); + auto item = make_item_for_dump_gimple_expr (stmt, 0, TDF_SLIM); + stash_item (formatted_tok_list, std::move (item)); return true; } @@ -973,8 +923,8 @@ dump_pretty_printer::decode_format (text_info *text, const char *spec, gimple *stmt = va_arg (*text->m_args_ptr, gimple *); /* Make an item for the stmt, and stash it. */ - optinfo_item *item = make_item_for_dump_gimple_stmt (stmt, 0, TDF_SLIM); - stash_item (buffer_ptr, item); + auto item = make_item_for_dump_gimple_stmt (stmt, 0, TDF_SLIM); + stash_item (formatted_tok_list, std::move (item)); return true; } @@ -983,8 +933,8 @@ dump_pretty_printer::decode_format (text_info *text, const char *spec, tree t = va_arg (*text->m_args_ptr, tree); /* Make an item for the tree, and stash it. */ - optinfo_item *item = make_item_for_dump_generic_expr (t, TDF_SLIM); - stash_item (buffer_ptr, item); + auto item = make_item_for_dump_generic_expr (t, TDF_SLIM); + stash_item (formatted_tok_list, std::move (item)); return true; } @@ -993,10 +943,92 @@ dump_pretty_printer::decode_format (text_info *text, const char *spec, } } +void +dump_pretty_printer::custom_token_printer:: +print_tokens (pretty_printer *pp, + const pp_token_list &tokens) +{ + /* Accumulate text whilst emitting items. */ + for (auto iter = tokens.m_first; iter; iter = iter->m_next) + switch (iter->m_kind) + { + default: + gcc_unreachable (); + + case pp_token::kind::text: + { + pp_token_text *sub = as_a <pp_token_text *> (iter); + gcc_assert (sub->m_value.get ()); + pp_string (pp, sub->m_value.get ()); + } + break; + + case pp_token::kind::begin_color: + case pp_token::kind::end_color: + /* No-op for dumpfiles. */ + 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; + + case pp_token::kind::begin_url: + case pp_token::kind::end_url: + /* No-op for dumpfiles. */ + break; + + case pp_token::kind::custom_data: + { + emit_any_pending_textual_chunks (); + pp_token_custom_data *sub = as_a <pp_token_custom_data *> (iter); + gcc_assert (sub->m_value.get ()); + wrapped_optinfo_item *custom_data + = static_cast<wrapped_optinfo_item *> (sub->m_value.get ()); + m_dump_pp.emit_item (std::move (custom_data->m_optinfo_item), + m_optinfo); + } + break; + } + + emit_any_pending_textual_chunks (); +} + +/* Subroutine of dump_pretty_printer::custom_token_printer::print_tokens + for consolidating multiple adjacent pure-text chunks into single + optinfo_items (in phase 3). */ + +void +dump_pretty_printer::custom_token_printer:: +emit_any_pending_textual_chunks () +{ + dump_pretty_printer *pp = &m_dump_pp; + output_buffer *const buffer = pp_buffer (pp); + gcc_assert (buffer->obstack == &buffer->formatted_obstack); + + /* Don't emit an item if the pending text is empty. */ + if (output_buffer_last_position_in_text (buffer) == nullptr) + return; + + char *formatted_text = xstrdup (pp_formatted_text (pp)); + std::unique_ptr<optinfo_item> item + = make_unique<optinfo_item> (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION, + formatted_text); + pp->emit_item (std::move (item), m_optinfo); + + /* Clear the pending text by unwinding formatted_text back to the start + of the buffer (without deallocating). */ + obstack_free (&buffer->formatted_obstack, + buffer->formatted_obstack.object_base); +} + /* Output a formatted message using FORMAT on appropriate dump streams. */ void -dump_context::dump_printf_va (const dump_metadata_t &metadata, const char *format, +dump_context::dump_printf_va (const dump_metadata_t &metadata, + const char *format, va_list *ap) { dump_pretty_printer pp (this, metadata.get_dump_flags ()); @@ -1006,14 +1038,16 @@ dump_context::dump_printf_va (const dump_metadata_t &metadata, const char *forma /* Phases 1 and 2, using pp_format. */ pp_format (&pp, &text); - /* Phase 3. */ + /* Phase 3: update the custom token_printer with any active optinfo. */ if (optinfo_enabled_p ()) { optinfo &info = ensure_pending_optinfo (metadata); - pp.emit_items (&info); + pp.set_optinfo (&info); } else - pp.emit_items (NULL); + pp.set_optinfo (nullptr); + + pp_output_formatted_text (&pp, nullptr); } /* Similar to dump_printf, except source location is also printed, and @@ -1031,7 +1065,7 @@ dump_context::dump_printf_loc_va (const dump_metadata_t &metadata, /* Make an item for the given dump call, equivalent to print_dec. */ template<unsigned int N, typename C> -static optinfo_item * +static std::unique_ptr<optinfo_item> make_item_for_dump_dec (const poly_int<N, C> &value) { STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0); @@ -1051,9 +1085,9 @@ make_item_for_dump_dec (const poly_int<N, C> &value) } } - optinfo_item *item - = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION, - xstrdup (pp_formatted_text (&pp))); + auto item + = make_unique<optinfo_item> (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION, + xstrdup (pp_formatted_text (&pp))); return item; } @@ -1061,35 +1095,33 @@ make_item_for_dump_dec (const poly_int<N, C> &value) template<unsigned int N, typename C> void -dump_context::dump_dec (const dump_metadata_t &metadata, const poly_int<N, C> &value) +dump_context::dump_dec (const dump_metadata_t &metadata, + const poly_int<N, C> &value) { - optinfo_item *item = make_item_for_dump_dec (value); - emit_item (item, metadata.get_dump_flags ()); + auto item = make_item_for_dump_dec (value); + emit_item (*item.get (), metadata.get_dump_flags ()); if (optinfo_enabled_p ()) { optinfo &info = ensure_pending_optinfo (metadata); - info.add_item (item); + info.add_item (std::move (item)); } - else - delete item; } /* Output the name of NODE on appropriate dump streams. */ void -dump_context::dump_symtab_node (const dump_metadata_t &metadata, symtab_node *node) +dump_context::dump_symtab_node (const dump_metadata_t &metadata, + symtab_node *node) { - optinfo_item *item = make_item_for_dump_symtab_node (node); - emit_item (item, metadata.get_dump_flags ()); + auto item = make_item_for_dump_symtab_node (node); + emit_item (*item.get (), metadata.get_dump_flags ()); if (optinfo_enabled_p ()) { optinfo &info = ensure_pending_optinfo (metadata); - info.add_item (item); + info.add_item (std::move (item)); } - else - delete item; } /* Get the current dump scope-nesting depth. @@ -1132,10 +1164,10 @@ dump_context::begin_scope (const char *name, pretty_printer pp; pp_printf (&pp, "%s %s %s", "===", name, "==="); pp_newline (&pp); - optinfo_item *item - = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION, - xstrdup (pp_formatted_text (&pp))); - emit_item (item, MSG_NOTE); + std::unique_ptr<optinfo_item> item + = make_unique<optinfo_item> (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION, + xstrdup (pp_formatted_text (&pp))); + emit_item (*item.get (), MSG_NOTE); if (optinfo_enabled_p ()) { @@ -1143,11 +1175,9 @@ dump_context::begin_scope (const char *name, = begin_next_optinfo (dump_metadata_t (MSG_NOTE, impl_location), user_location); info.m_kind = OPTINFO_KIND_SCOPE; - info.add_item (item); + info.add_item (std::move (item)); end_any_optinfo (); } - else - delete item; } /* Pop a nested dump scope. */ @@ -1226,17 +1256,17 @@ dump_context::emit_optinfo (const optinfo *info) consolidation into optinfo instances). */ void -dump_context::emit_item (optinfo_item *item, dump_flags_t dump_kind) +dump_context::emit_item (const optinfo_item &item, dump_flags_t dump_kind) { if (dump_file && apply_dump_filter_p (dump_kind, pflags)) - fprintf (dump_file, "%s", item->get_text ()); + fprintf (dump_file, "%s", item.get_text ()); if (alt_dump_file && apply_dump_filter_p (dump_kind, alt_flags)) - fprintf (alt_dump_file, "%s", item->get_text ()); + fprintf (alt_dump_file, "%s", item.get_text ()); /* Support for temp_dump_context in selftests. */ if (m_test_pp && apply_dump_filter_p (dump_kind, m_test_pp_flags)) - pp_string (m_test_pp, item->get_text ()); + pp_string (m_test_pp, item.get_text ()); } /* The current singleton dump_context, and its default. */ diff --git a/gcc/fortran/error.cc b/gcc/fortran/error.cc index e896676..a588462 100644 --- a/gcc/fortran/error.cc +++ b/gcc/fortran/error.cc @@ -1125,7 +1125,7 @@ gfc_notify_std (int std, const char *gmsgid, ...) static bool gfc_format_decoder (pretty_printer *pp, text_info *text, const char *spec, int precision, bool wide, bool set_locus, bool hash, - bool *quoted, const char **buffer_ptr) + bool *quoted, pp_token_list &formatted_token_list) { switch (*spec) { @@ -1170,7 +1170,8 @@ gfc_format_decoder (pretty_printer *pp, text_info *text, const char *spec, etc. diagnostics can use the FE printer while the FE is still active. */ return default_tree_printer (pp, text, spec, precision, wide, - set_locus, hash, quoted, buffer_ptr); + set_locus, hash, quoted, + formatted_token_list); } } diff --git a/gcc/fortran/trans-io.cc b/gcc/fortran/trans-io.cc index 7ab82fa..c0baa71 100644 --- a/gcc/fortran/trans-io.cc +++ b/gcc/fortran/trans-io.cc @@ -1692,7 +1692,8 @@ transfer_namelist_element (stmtblock_t * block, const char * var_name, gcc_assert (sym || c); /* Build the namelist object name. */ - if (sym && !sym->attr.use_only && sym->attr.use_rename) + if (sym && !sym->attr.use_only && sym->attr.use_rename + && sym->ns->use_stmts->rename) string = gfc_build_cstring_const (sym->ns->use_stmts->rename->local_name); else string = gfc_build_cstring_const (var_name); diff --git a/gcc/gimple-loop-interchange.cc b/gcc/gimple-loop-interchange.cc index b422804..a4ea818 100644 --- a/gcc/gimple-loop-interchange.cc +++ b/gcc/gimple-loop-interchange.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/gimple-loop-jam.cc b/gcc/gimple-loop-jam.cc index 306d5ce..bf01e0b 100644 --- a/gcc/gimple-loop-jam.cc +++ b/gcc/gimple-loop-jam.cc @@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "tree-pass.h" diff --git a/gcc/gimple-loop-versioning.cc b/gcc/gimple-loop-versioning.cc index adea207..107b002 100644 --- a/gcc/gimple-loop-versioning.cc +++ b/gcc/gimple-loop-versioning.cc @@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/graphite-dependences.cc b/gcc/graphite-dependences.cc index a35f712..41e1114 100644 --- a/gcc/graphite-dependences.cc +++ b/gcc/graphite-dependences.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #define INCLUDE_ISL +#define INCLUDE_MEMORY #include "config.h" diff --git a/gcc/graphite-isl-ast-to-gimple.cc b/gcc/graphite-isl-ast-to-gimple.cc index a27402b..ff539b1 100644 --- a/gcc/graphite-isl-ast-to-gimple.cc +++ b/gcc/graphite-isl-ast-to-gimple.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #define INCLUDE_ISL +#define INCLUDE_MEMORY #include "config.h" diff --git a/gcc/graphite-optimize-isl.cc b/gcc/graphite-optimize-isl.cc index 2222dd6..ff3dd6b 100644 --- a/gcc/graphite-optimize-isl.cc +++ b/gcc/graphite-optimize-isl.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #define INCLUDE_ISL +#define INCLUDE_MEMORY #include "config.h" diff --git a/gcc/graphite-poly.cc b/gcc/graphite-poly.cc index c78ff98..76aba03 100644 --- a/gcc/graphite-poly.cc +++ b/gcc/graphite-poly.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #define INCLUDE_ISL +#define INCLUDE_MEMORY #include "config.h" diff --git a/gcc/graphite-scop-detection.cc b/gcc/graphite-scop-detection.cc index 9e44f10..de7c111 100644 --- a/gcc/graphite-scop-detection.cc +++ b/gcc/graphite-scop-detection.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #define INCLUDE_ISL +#define INCLUDE_MEMORY #include "config.h" diff --git a/gcc/graphite-sese-to-poly.cc b/gcc/graphite-sese-to-poly.cc index 5ce8985..1e7818a 100644 --- a/gcc/graphite-sese-to-poly.cc +++ b/gcc/graphite-sese-to-poly.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #define INCLUDE_ISL +#define INCLUDE_MEMORY #include "config.h" diff --git a/gcc/graphite.cc b/gcc/graphite.cc index 80e6a5d..65b970f 100644 --- a/gcc/graphite.cc +++ b/gcc/graphite.cc @@ -28,6 +28,7 @@ along with GCC; see the file COPYING3. If not see the related work. */ #define INCLUDE_ISL +#define INCLUDE_MEMORY #include "config.h" #include "system.h" diff --git a/gcc/opt-problem.cc b/gcc/opt-problem.cc index f40f481..fc29333 100644 --- a/gcc/opt-problem.cc +++ b/gcc/opt-problem.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" @@ -70,7 +71,8 @@ opt_problem::opt_problem (const dump_location_t &loc, /* Phase 3: dump the items to the "immediate" dump destinations, and storing them into m_optinfo for later retrieval. */ - pp.emit_items (&m_optinfo); + pp.set_optinfo (&m_optinfo); + pp_output_formatted_text (&pp, nullptr); } } diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc index 7a82561..48a270cbf 100644 --- a/gcc/optinfo.cc +++ b/gcc/optinfo.cc @@ -84,10 +84,10 @@ optinfo::~optinfo () /* Add ITEM to this optinfo. */ void -optinfo::add_item (optinfo_item *item) +optinfo::add_item (std::unique_ptr<optinfo_item> item) { - gcc_assert (item); - m_items.safe_push (item); + gcc_assert (item.get ()); + m_items.safe_push (item.release ()); } /* Get MSG_* flags corresponding to KIND. */ @@ -123,7 +123,7 @@ optinfo::emit_for_opt_problem () const unsigned i; optinfo_item *item; FOR_EACH_VEC_ELT (m_items, i, item) - dump_context::get ().emit_item (item, dump_kind); + dump_context::get ().emit_item (*item, dump_kind); /* Re-emit to "non-immediate" destinations. */ dump_context::get ().emit_optinfo (this); diff --git a/gcc/optinfo.h b/gcc/optinfo.h index 986ef75..db92294 100644 --- a/gcc/optinfo.h +++ b/gcc/optinfo.h @@ -21,6 +21,15 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_OPTINFO_H #define GCC_OPTINFO_H +/* This header uses std::unique_ptr, but <memory> can't be directly + included due to issues with macros. Hence <memory> must be included + from system.h by defining INCLUDE_MEMORY in any source file using + optinfo.h. */ + +#ifndef INCLUDE_MEMORY +# error "You must define INCLUDE_MEMORY before including system.h to use optinfo.h" +#endif + /* An "optinfo" is a bundle of information describing part of an optimization, which can be emitted to zero or more of several destinations, such as: @@ -119,7 +128,7 @@ class optinfo location_t get_location_t () const { return m_loc.get_location_t (); } profile_count get_count () const { return m_loc.get_count (); } - void add_item (optinfo_item *item); + void add_item (std::unique_ptr<optinfo_item> item); void emit_for_opt_problem () const; diff --git a/gcc/pretty-print-format-impl.h b/gcc/pretty-print-format-impl.h new file mode 100644 index 0000000..f199628 --- /dev/null +++ b/gcc/pretty-print-format-impl.h @@ -0,0 +1,489 @@ +/* Implementation detail of pp_format. + Copyright (C) 2002-2024 Free Software Foundation, Inc. + Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net> + +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/>. */ + +#ifndef GCC_PRETTY_PRINT_FORMAT_IMPL_H +#define GCC_PRETTY_PRINT_FORMAT_IMPL_H + +#include "pretty-print.h" +#include "diagnostic-event-id.h" + +/* A struct representing a pending item to be printed within + pp_format. + + These can represent: + - a run of text within one of the output_buffers's obstacks + - begin/end named color + - open/close quote + - begin/end URL + - event IDs + - custom data (for the formatter, for the pretty_printer, + or the output format) + + These are built into pp_token_list instances. + + Doing so allows for interaction between: + + - pretty_printer formatting codes (such as C++'s %H and %I, + which can't be printed until we've seen both) + + - output formats, such as text vs SARIF (so each can handle URLs + and event IDs it its own way) + + - optimization records, where we want to stash data into the + formatted messages + + - urlifiers: these can be run in phase 3 of formatting + + without needing lots of fragile logic on char pointers. + + To avoid needing lots of heap allocation/deallocation, pp_token + instances are allocated in the pretty_printer's chunk_obstack: + they must not outlive phase 3 of formatting of the given + chunk_info level. */ + +struct pp_token +{ +public: + enum class kind + { + text, + + begin_color, + end_color, + + begin_quote, + end_quote, + + begin_url, + end_url, + + event_id, + + custom_data, + + NUM_KINDS + }; + + pp_token (enum kind k); + + pp_token (const pp_token &) = delete; + pp_token (pp_token &&) = delete; + + virtual ~pp_token () = default; + + pp_token &operator= (const pp_token &) = delete; + pp_token &operator= (pp_token &&) = delete; + + void dump (FILE *out) const; + void DEBUG_FUNCTION dump () const { dump (stderr); } + + static void *operator new (size_t sz, obstack &s); + static void operator delete (void *); + + enum kind m_kind; + + // Intrusive doubly-linked list + pp_token *m_prev; + pp_token *m_next; +}; + +/* Subclasses of pp_token for the various kinds of token. */ + +struct pp_token_text : public pp_token +{ + pp_token_text (label_text &&value) + : pp_token (kind::text), + m_value (std::move (value)) + { + gcc_assert (m_value.get ()); + } + + label_text m_value; +}; + +template <> +template <> +inline bool +is_a_helper <pp_token_text *>::test (pp_token *tok) +{ + return tok->m_kind == pp_token::kind::text; +} + +template <> +template <> +inline bool +is_a_helper <const pp_token_text *>::test (const pp_token *tok) +{ + return tok->m_kind == pp_token::kind::text; +} + +struct pp_token_begin_color : public pp_token +{ + pp_token_begin_color (label_text &&value) + : pp_token (kind::begin_color), + m_value (std::move (value)) + { + gcc_assert (m_value.get ()); + } + + label_text m_value; +}; + +template <> +template <> +inline bool +is_a_helper <pp_token_begin_color *>::test (pp_token *tok) +{ + return tok->m_kind == pp_token::kind::begin_color; +} + +template <> +template <> +inline bool +is_a_helper <const pp_token_begin_color *>::test (const pp_token *tok) +{ + return tok->m_kind == pp_token::kind::begin_color; +} + +struct pp_token_end_color : public pp_token +{ + pp_token_end_color () + : pp_token (kind::end_color) + { + } +}; + +struct pp_token_begin_quote : public pp_token +{ + pp_token_begin_quote () + : pp_token (kind::begin_quote) + { + } +}; + +struct pp_token_end_quote : public pp_token +{ + pp_token_end_quote () + : pp_token (kind::end_quote) + { + } +}; + +struct pp_token_begin_url : public pp_token +{ + pp_token_begin_url (label_text &&value) + : pp_token (kind::begin_url), + m_value (std::move (value)) + { + gcc_assert (m_value.get ()); + } + + label_text m_value; +}; + +template <> +template <> +inline bool +is_a_helper <pp_token_begin_url*>::test (pp_token *tok) +{ + return tok->m_kind == pp_token::kind::begin_url; +} + +template <> +template <> +inline bool +is_a_helper <const pp_token_begin_url*>::test (const pp_token *tok) +{ + return tok->m_kind == pp_token::kind::begin_url; +} + +struct pp_token_end_url : public pp_token +{ + pp_token_end_url () + : pp_token (kind::end_url) + { + } +}; + +struct pp_token_event_id : public pp_token +{ + pp_token_event_id (diagnostic_event_id_t event_id) + : pp_token (kind::event_id), + m_event_id (event_id) + { + gcc_assert (event_id.known_p ()); + } + + diagnostic_event_id_t m_event_id; +}; + +template <> +template <> +inline bool +is_a_helper <pp_token_event_id *>::test (pp_token *tok) +{ + return tok->m_kind == pp_token::kind::event_id; +} + +template <> +template <> +inline bool +is_a_helper <const pp_token_event_id *>::test (const pp_token *tok) +{ + return tok->m_kind == pp_token::kind::event_id; +} + +struct pp_token_custom_data : public pp_token +{ + class value + { + public: + virtual ~value () {} + virtual void dump (FILE *out) const = 0; + + /* Hook for lowering a custom_data token to standard tokens. + Return true and write to OUT if possible. + Return false for custom_data that is to be handled by + the token_printer. */ + virtual bool as_standard_tokens (pp_token_list &out) = 0; + }; + + pp_token_custom_data (std::unique_ptr<value> val) + : pp_token (kind::custom_data), + m_value (std::move (val)) + { + gcc_assert (m_value.get ()); + } + + std::unique_ptr<value> m_value; +}; + +template <> +template <> +inline bool +is_a_helper <pp_token_custom_data *>::test (pp_token *tok) +{ + return tok->m_kind == pp_token::kind::custom_data; +} + +template <> +template <> +inline bool +is_a_helper <const pp_token_custom_data *>::test (const pp_token *tok) +{ + return tok->m_kind == pp_token::kind::custom_data; +} + +/* A list of pp_token, with ownership of the tokens, using + a particular obstack to allocate its tokens. These are + also allocated on the obstack during formatting (or, occasionally, + the stack). */ + +class pp_token_list +{ +public: + // Allocate a new pp_token_list within S. + static pp_token_list *make (obstack &s) + { + return new (s) pp_token_list (s); + } + static void *operator new (size_t sz, obstack &s); + static void operator delete (void *); + + pp_token_list (obstack &s); + pp_token_list (const pp_token_list &) = delete; + pp_token_list (pp_token_list &&); + + ~pp_token_list (); + + pp_token &operator= (const pp_token_list &) = delete; + pp_token &operator= (pp_token_list &&) = delete; + +/* Make a pp_token of the given subclass, using the relevant obstack to provide + the memory. The pp_token must therefore not outlive the current chunk_info + level during formatting. */ + template<typename Subclass, typename... Args> + std::unique_ptr<pp_token> + make_token (Args&&... args) + { + return std::unique_ptr<pp_token> + (new (m_obstack) Subclass (std::forward<Args> (args)...)); + } + + template<typename Subclass, typename... Args> + void push_back (Args&&... args) + { + auto tok = make_token<Subclass> (std::forward<Args> (args)...); + push_back (std::move (tok)); + } + void push_back_text (label_text &&text); + void push_back (std::unique_ptr<pp_token> tok); + void push_back_list (pp_token_list &&list); + + std::unique_ptr<pp_token> pop_front (); + + std::unique_ptr<pp_token> remove_token (pp_token *tok); + + void insert_after (std::unique_ptr<pp_token> new_tok, + pp_token *relative_tok); + + void replace_custom_tokens (); + void merge_consecutive_text_tokens (); + void apply_urlifier (const urlifier &urlifier); + + void dump (FILE *out) const; + void DEBUG_FUNCTION dump () const { dump (stderr); } + + obstack &m_obstack; + + pp_token *m_first; + pp_token *m_end; +}; + +/* The chunk_info data structure forms a stack of the results from the + first phase of formatting (pp_format) which have not yet been + output (pp_output_formatted_text). A stack is necessary because + the diagnostic starter may decide to generate its own output by way + of the formatter. */ +class chunk_info +{ + friend class pretty_printer; + friend class pp_markup::context; + +public: + pp_token_list * const * get_token_lists () const { return m_args; } + + void append_formatted_chunk (obstack &s, const char *content); + + void pop_from_output_buffer (output_buffer &buf); + + void dump (FILE *out) const; + void DEBUG_FUNCTION dump () const { dump (stderr); } + +private: + void on_begin_quote (const output_buffer &buf, + unsigned chunk_idx, + const urlifier *urlifier); + + void on_end_quote (pretty_printer *pp, + output_buffer &buf, + unsigned chunk_idx, + const urlifier *urlifier); + + /* Pointer to previous chunk on the stack. */ + chunk_info *m_prev; + + /* Array of chunks to output. Each chunk is a doubly-linked list of + pp_token. + + The chunks can be printed via chunk_info::dump (). + + In the first phase of formatting, even-numbered chunks are + to be output verbatim, odd-numbered chunks are format specifiers. + For example, given: + pp_format (pp, + "foo: %i, bar: %s, opt: %qs", + 42, "baz", "-foption"); + + after phase 1 we might have: + (gdb) call buffer->cur_chunk_array->dump() + 0: [TEXT("foo: ")] + 1: [TEXT("i")] + 2: [TEXT(", bar: ")] + 3: [TEXT("s")] + 4: [TEXT(", opt: ")] + 5: [TEXT("qs")] + + The second phase replaces all odd-numbered chunks with formatted + token lists. In the above example, after phase 2 we might have: + (gdb) call pp->m_buffer->cur_chunk_array->dump() + 0: [TEXT("foo: ")] + 1: [TEXT("42")] + 2: [TEXT(", bar: ")] + 3: [TEXT("baz")] + 4: [TEXT(", opt: ")] + 5: [BEGIN_QUOTE, TEXT("-foption"), END_QUOTE] + For example the %qs has become the three tokens: + [BEGIN_QUOTE, TEXT("-foption"), END_QUOTE] + + The third phase (in pp_output_formatted_text): + + (1) merges the tokens from all the chunks into one list, + giving e.g. + (gdb) call tokens.dump() + [TEXT("foo: "), TEXT("42"), TEXT(", bar: "), TEXT("baz"), + TEXT(", opt: "), BEGIN_QUOTE, TEXT("-foption"), END_QUOTE] + + (2) lowers some custom tokens into non-custom tokens + + (3) merges consecutive text tokens, giving e.g.: + (gdb) call tokens.dump() + [TEXT("foo: 42, bar: baz, option: "), + BEGIN_QUOTE, TEXT("-foption"), END_QUOTE] + + (4) if provided with a urlifier, tries to apply it to quoted text, + giving e.g: + (gdb) call tokens.dump() + [TEXT("foo: 42, bar: baz, option: "), BEGIN_QUOTE, + BEGIN_URL("http://example.com"), TEXT("-foption"), END_URL, END_QUOTE] + + (5) emits all tokens in sequence with appropriate line-wrapping. This + can be overridded via the pretty_printer's token_printer, allowing for + output formats to e.g. override how URLs are handled, or to handle + custom_data that wasn't lowered in (2) above, e.g. for handling JSON + output of optimization records. */ + pp_token_list *m_args[PP_NL_ARGMAX * 2]; + + /* The pp_tokens, pp_token_lists, and the accumulated text buffers are + allocated within the output_buffer's chunk_obstack. In the above + example, the in-memory layout of the chunk_obstack might look like + this after phase 1: + + + pp_token_list for chunk 0 (m_first: *) <--- START of chunk_info level + | | + + "foo: \0" <-------------\ | + | | | + + pp_token_text (borrowed: *) <-------/ + | + + pp_token_list for chunk 1 + | + + "i\0" <------------------\ + | | + + pp_token_text (borrowed: *) + | + + ...etc for chunks 2 to 4... + | + + pp_token_list for chunk 5 + | + + "qs\0" <-----------------\ + | | + + pp_token_text (borrowed: *) + | + | + V + obstack grows this way + + At each stage, allocation of additional text buffers, tokens, and lists + grow forwards in the obstack (though the internal pointers in linked + lists might point backwards to earlier objects within the same + chunk_info level). */ +}; + +#endif /* GCC_PRETTY_PRINT_FORMAT_IMPL_H */ diff --git a/gcc/pretty-print-markup.h b/gcc/pretty-print-markup.h index b35632a..ce2c5e9 100644 --- a/gcc/pretty-print-markup.h +++ b/gcc/pretty-print-markup.h @@ -22,6 +22,8 @@ along with GCC; see the file COPYING3. If not see #include "diagnostic-color.h" +class pp_token_list; + namespace pp_markup { class context @@ -31,12 +33,12 @@ public: output_buffer &buf, unsigned chunk_idx, bool "ed, - const urlifier *urlifier) + pp_token_list *formatted_token_list) : m_pp (pp), m_buf (buf), m_chunk_idx (chunk_idx), m_quoted (quoted), - m_urlifier (urlifier) + m_formatted_token_list (formatted_token_list) { } @@ -46,11 +48,13 @@ public: void begin_highlight_color (const char *color_name); void end_highlight_color (); + void push_back_any_text (); + pretty_printer &m_pp; output_buffer &m_buf; unsigned m_chunk_idx; bool &m_quoted; - const urlifier *m_urlifier; + pp_token_list *m_formatted_token_list; }; /* Abstract base class for use in pp_format for handling "%e". diff --git a/gcc/pretty-print-urlifier.h b/gcc/pretty-print-urlifier.h index 3e63e62..3feb809 100644 --- a/gcc/pretty-print-urlifier.h +++ b/gcc/pretty-print-urlifier.h @@ -20,7 +20,7 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_PRETTY_PRINT_URLIFIER_H #define GCC_PRETTY_PRINT_URLIFIER_H -/* Abstract base class for optional use in pp_format for adding URLs +/* Abstract base class for optional use in pretty-printing for adding URLs to quoted text strings. */ class urlifier diff --git a/gcc/pretty-print.cc b/gcc/pretty-print.cc index 1d91da8..fe6b6090 100644 --- a/gcc/pretty-print.cc +++ b/gcc/pretty-print.cc @@ -19,16 +19,19 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #define INCLUDE_VECTOR #include "system.h" #include "coretypes.h" #include "intl.h" #include "pretty-print.h" +#include "pretty-print-format-impl.h" #include "pretty-print-markup.h" #include "pretty-print-urlifier.h" #include "diagnostic-color.h" #include "diagnostic-event-id.h" #include "diagnostic-highlight-colors.h" +#include "make-unique.h" #include "selftest.h" #if HAVE_ICONV @@ -709,6 +712,10 @@ static int decode_utf8_char (const unsigned char *, size_t len, unsigned int *); static void pp_quoted_string (pretty_printer *, const char *, size_t = -1); +static void +default_token_printer (pretty_printer *pp, + const pp_token_list &tokens); + /* Overwrite the given location/range within this text_info's rich_location. For use e.g. when implementing "+" in client format decoders. */ @@ -1062,196 +1069,418 @@ pp_indent (pretty_printer *pp) static const char *get_end_url_string (pretty_printer *); -/* Append STR to OSTACK, without a null-terminator. */ +/* struct pp_token. */ -static void -obstack_append_string (obstack *ostack, const char *str) +pp_token::pp_token (enum kind k) +: m_kind (k), + m_prev (nullptr), + m_next (nullptr) { - obstack_grow (ostack, str, strlen (str)); } -/* Append STR to OSTACK, without a null-terminator. */ - -static void -obstack_append_string (obstack *ostack, const char *str, size_t len) -{ - obstack_grow (ostack, str, len); -} - -/* Given quoted text within the buffer OBSTACK - at the half-open interval [QUOTED_TEXT_START_IDX, QUOTED_TEXT_END_IDX), - potentially use URLIFIER (if non-null) to see if there's a URL for the - quoted text. - - If so, replace the quoted part of the text in the buffer with a URLified - version of the text, using PP's settings. - - For example, given this is the buffer: - "this is a test `hello worldTRAILING-CONTENT" - .................^~~~~~~~~~~ - with the quoted text starting at the 'h' of "hello world", the buffer - becomes: - "this is a test `BEGIN_URL(URL)hello worldEND(URL)TRAILING-CONTENT" - .................^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - .................-----------replacement----------- - - Return the new offset into the buffer of the quoted text endpoint i.e. - the offset of "TRAILING-CONTENT" in the above. */ - -static size_t -urlify_quoted_string (pretty_printer *pp, - obstack *obstack, - const urlifier *urlifier, - size_t quoted_text_start_idx, - size_t quoted_text_end_idx) -{ - if (!pp->supports_urls_p ()) - return quoted_text_end_idx; - if (!urlifier) - return quoted_text_end_idx; - - const size_t quoted_len = quoted_text_end_idx - quoted_text_start_idx; - if (quoted_len == 0) - /* Empty quoted string; do nothing. */ - return quoted_text_end_idx; - const char *start = (obstack->object_base + quoted_text_start_idx); - char *url = urlifier->get_url_for_quoted_text (start, quoted_len); - if (!url) - /* No URL for this quoted text; do nothing. */ - return quoted_text_end_idx; - - /* Stash a copy of the remainder of the chunk. */ - char *text = xstrndup (start, - obstack_object_size (obstack) - quoted_text_start_idx); - - /* Replace quoted text... */ - obstack->next_free = obstack->object_base + quoted_text_start_idx; - - /* ...with URLified version of the text. */ - /* Begin URL. */ - switch (pp->get_url_format ()) +void +pp_token::dump (FILE *out) const +{ + switch (m_kind) { default: - case URL_FORMAT_NONE: gcc_unreachable (); - case URL_FORMAT_ST: - obstack_append_string (obstack, "\33]8;;"); - obstack_append_string (obstack, url); - obstack_append_string (obstack, "\33\\"); + case kind::text: + { + const pp_token_text *sub = as_a <const pp_token_text *> (this); + gcc_assert (sub->m_value.get ()); + fprintf (out, "TEXT(\"%s\")", sub->m_value.get ()); + } break; - case URL_FORMAT_BEL: - obstack_append_string (obstack, "\33]8;;"); - obstack_append_string (obstack, url); - obstack_append_string (obstack, "\a"); + case kind::begin_color: + { + const pp_token_begin_color *sub + = as_a <const pp_token_begin_color *> (this); + gcc_assert (sub->m_value.get ()); + fprintf (out, "BEGIN_COLOR(\"%s\")", sub->m_value.get ()); + break; + } + case kind::end_color: + fprintf (out, "END_COLOR"); + break; + case kind::begin_quote: + fprintf (out, "BEGIN_QUOTE"); + break; + case kind::end_quote: + fprintf (out, "END_QUOTE"); + break; + case kind::begin_url: + { + const pp_token_begin_url *sub + = as_a <const pp_token_begin_url *> (this); + gcc_assert (sub->m_value.get ()); + fprintf (out, "BEGIN_URL(\"%s\")", sub->m_value.get ()); + } + break; + case kind::end_url: + fprintf (out, "END_URL"); + break; + + case kind::event_id: + { + const pp_token_event_id *sub + = as_a <const pp_token_event_id *> (this); + gcc_assert (sub->m_event_id.known_p ()); + fprintf (out, "EVENT((%i))", sub->m_event_id.one_based ()); + } + break; + + case kind::custom_data: + { + const pp_token_custom_data *sub + = as_a <const pp_token_custom_data *> (this); + gcc_assert (sub->m_value.get ()); + fprintf (out, "CUSTOM("); + sub->m_value->dump (out); + fprintf (out, ")"); + } break; } - /* Add back the quoted part of the text. */ - obstack_append_string (obstack, text, quoted_len); - /* End URL. */ - obstack_append_string (obstack, - get_end_url_string (pp)); +} + +/* Allocate SZ bytes within S, which must not be half-way through + building another object. */ - size_t new_end_idx = obstack_object_size (obstack); +static void * +allocate_object (size_t sz, obstack &s) +{ + /* We must not be half-way through an object. */ + gcc_assert (obstack_base (&s) == obstack_next_free (&s)); - /* Add back the remainder of the text after the quoted part. */ - obstack_append_string (obstack, text + quoted_len); - free (text); - free (url); - return new_end_idx; + obstack_grow (&s, obstack_base (&s), sz); + void *buf = obstack_finish (&s); + return buf; } -/* A class for tracking quoted text within a buffer for - use by a urlifier. */ +/* Make room for a pp_token instance within obstack S. */ -class quoting_info +void * +pp_token::operator new (size_t sz, obstack &s) { -public: - /* Called when quoted text is begun in phase 1 or 2. */ - void on_begin_quote (const output_buffer &buf, - unsigned chunk_idx) - { - /* Stash location of start of quoted string. */ - size_t byte_offset = obstack_object_size (&buf.chunk_obstack); - m_loc_last_open_quote = location (chunk_idx, byte_offset); - } + return allocate_object (sz, s); +} - /* Called when quoted text is ended in phase 1 or 2. */ - void on_end_quote (pretty_printer *pp, - output_buffer &buf, - unsigned chunk_idx, - const urlifier &urlifier) - { - /* If possible, do urlification now. */ - if (chunk_idx == m_loc_last_open_quote.m_chunk_idx) - { - urlify_quoted_string (pp, - &buf.chunk_obstack, - &urlifier, - m_loc_last_open_quote.m_byte_offset, - obstack_object_size (&buf.chunk_obstack)); - m_loc_last_open_quote = location (); - return; - } - /* Otherwise the quoted text straddles multiple chunks. - Stash the location of end of quoted string for use in phase 3. */ - size_t byte_offset = obstack_object_size (&buf.chunk_obstack); - m_phase_3_quotes.push_back (run (m_loc_last_open_quote, - location (chunk_idx, byte_offset))); - m_loc_last_open_quote = location (); - } +void +pp_token::operator delete (void *) +{ + /* No-op: pp_tokens are allocated within obstacks, so + the memory will be reclaimed when the obstack is freed. */ +} - bool has_phase_3_quotes_p () const - { - return m_phase_3_quotes.size () > 0; - } - void handle_phase_3 (pretty_printer *pp, - const urlifier &urlifier); +/* class pp_token_list. */ -private: - struct location - { - location () - : m_chunk_idx (UINT_MAX), - m_byte_offset (SIZE_MAX) +/* Make room for a pp_token_list instance within obstack S. */ + +void * +pp_token_list::operator new (size_t sz, obstack &s) +{ + return allocate_object (sz, s); +} + +void +pp_token_list::operator delete (void *) +{ + /* No-op: pp_token_list allocated within obstacks don't + need their own reclaim the memory will be reclaimed when + the obstack is freed. */ +} + +pp_token_list::pp_token_list (obstack &s) +: m_obstack (s), + m_first (nullptr), + m_end (nullptr) +{ +} + +pp_token_list::pp_token_list (pp_token_list &&other) +: m_obstack (other.m_obstack), + m_first (other.m_first), + m_end (other.m_end) +{ + other.m_first = nullptr; + other.m_end = nullptr; +} + +pp_token_list::~pp_token_list () +{ + for (auto iter = m_first; iter; ) { + pp_token *next = iter->m_next; + delete iter; + iter = next; } +} + +void +pp_token_list::push_back_text (label_text &&text) +{ + if (text.get ()[0] == '\0') + return; // pushing empty string is a no-op + push_back<pp_token_text> (std::move (text)); +} - location (unsigned chunk_idx, - size_t byte_offset) - : m_chunk_idx (chunk_idx), - m_byte_offset (byte_offset) +void +pp_token_list::push_back (std::unique_ptr<pp_token> tok) +{ + if (!m_first) + { + gcc_assert (m_end == nullptr); + m_first = tok.get (); + m_end = tok.get (); + } + else { + gcc_assert (m_end != nullptr); + m_end->m_next = tok.get (); + tok->m_prev = m_end; + m_end = tok.get (); } + tok.release (); +} - unsigned m_chunk_idx; - size_t m_byte_offset; - }; +void +pp_token_list::push_back_list (pp_token_list &&list) +{ + while (auto tok = list.pop_front ()) + push_back (std::move (tok)); +} - struct run - { - run (location start, location end) - : m_start (start), m_end (end) +std::unique_ptr<pp_token> +pp_token_list::pop_front () +{ + pp_token *result = m_first; + if (result == nullptr) + return nullptr; + + gcc_assert (result->m_prev == nullptr); + m_first = result->m_next; + if (result->m_next) + { + gcc_assert (result != m_end); + m_first->m_prev = nullptr; + } + else { + gcc_assert (result == m_end); + m_end = nullptr; } + result->m_next = nullptr; + return std::unique_ptr<pp_token> (result); +} - location m_start; - location m_end; - }; +std::unique_ptr<pp_token> +pp_token_list::remove_token (pp_token *tok) +{ + gcc_assert (tok); + if (tok->m_prev) + { + gcc_assert (tok != m_first); + tok->m_prev->m_next = tok->m_next; + } + else + { + gcc_assert (tok == m_first); + m_first = tok->m_next; + } + if (tok->m_next) + { + gcc_assert (tok != m_end); + tok->m_next->m_prev = tok->m_prev; + } + else + { + gcc_assert (tok == m_end); + m_end = tok->m_prev; + } + tok->m_prev = nullptr; + tok->m_next = nullptr; + gcc_assert (m_first != tok); + gcc_assert (m_end != tok); + return std::unique_ptr<pp_token> (tok); +} + +/* Insert NEW_TOK after RELATIVE_TOK. */ + +void +pp_token_list::insert_after (std::unique_ptr<pp_token> new_tok_up, + pp_token *relative_tok) +{ + pp_token *new_tok = new_tok_up.release (); + + gcc_assert (new_tok); + gcc_assert (new_tok->m_prev == nullptr); + gcc_assert (new_tok->m_next == nullptr); + gcc_assert (relative_tok); + + if (relative_tok->m_next) + { + gcc_assert (relative_tok != m_end); + relative_tok->m_next->m_prev = new_tok; + } + else + { + gcc_assert (relative_tok == m_end); + m_end = new_tok; + } + new_tok->m_prev = relative_tok; + new_tok->m_next = relative_tok->m_next; + relative_tok->m_next = new_tok; +} + +void +pp_token_list::replace_custom_tokens () +{ + pp_token *iter = m_first; + while (iter) + { + pp_token *next = iter->m_next; + if (iter->m_kind == pp_token::kind::custom_data) + { + pp_token_list tok_list (m_obstack); + pp_token_custom_data *sub = as_a <pp_token_custom_data *> (iter); + if (sub->m_value->as_standard_tokens (tok_list)) + { + while (auto tok = tok_list.pop_front ()) + { + /* The resulting token list must not contain any + custom data. */ + gcc_assert (tok->m_kind != pp_token::kind::custom_data); + insert_after (std::move (tok), iter); + } + remove_token (iter); + } + } + iter = next; + } +} + +/* Merge any runs of consecutive text tokens within this list + into individual text tokens. */ + +void +pp_token_list::merge_consecutive_text_tokens () +{ + pp_token *start_of_run = m_first; + while (start_of_run) + { + if (start_of_run->m_kind != pp_token::kind::text) + { + start_of_run = start_of_run->m_next; + continue; + } + pp_token *end_of_run = start_of_run; + while (end_of_run->m_next + && end_of_run->m_next->m_kind == pp_token::kind::text) + end_of_run = end_of_run->m_next; + if (end_of_run != start_of_run) + { + /* start_of_run through end_of_run are a run of consecutive + text tokens. */ + + /* Calculate size of buffer for merged text. */ + size_t sz = 0; + for (auto iter = start_of_run; iter != end_of_run->m_next; + iter = iter->m_next) + { + pp_token_text *iter_text = static_cast<pp_token_text *> (iter); + sz += strlen (iter_text->m_value.get ()); + } + + /* Allocate and populate buffer for merged text + (within m_obstack). */ + char * const buf = (char *)allocate_object (sz + 1, m_obstack); + char *p = buf; + for (auto iter = start_of_run; iter != end_of_run->m_next; + iter = iter->m_next) + { + pp_token_text *iter_text = static_cast<pp_token_text *> (iter); + size_t iter_sz = strlen (iter_text->m_value.get ()); + memcpy (p, iter_text->m_value.get (), iter_sz); + p += iter_sz; + } + *p = '\0'; + + /* Replace start_of_run's buffer pointer with the new buffer. */ + static_cast<pp_token_text *> (start_of_run)->m_value + = label_text::borrow (buf); + + /* Remove all the other text tokens in the run. */ + pp_token * const next = end_of_run->m_next; + while (start_of_run->m_next != next) + remove_token (start_of_run->m_next); + start_of_run = next; + } + else + start_of_run = end_of_run->m_next; + } +} + +/* Apply URLIFIER to this token list. + Find BEGIN_QUOTE, TEXT, END_QUOTE triples, and if URLIFIER has a url + for the value of TEXT, then wrap TEXT in a {BEGIN,END}_URL pair. */ + +void +pp_token_list::apply_urlifier (const urlifier &urlifier) +{ + for (pp_token *iter = m_first; iter; ) + { + if (iter->m_kind == pp_token::kind::begin_quote + && iter->m_next + && iter->m_next->m_kind == pp_token::kind::text + && iter->m_next->m_next + && iter->m_next->m_next->m_kind == pp_token::kind::end_quote) + { + pp_token *begin_quote = iter; + pp_token_text *text = as_a <pp_token_text *> (begin_quote->m_next); + pp_token *end_quote = text->m_next; + if (char *url = urlifier.get_url_for_quoted_text + (text->m_value.get (), + strlen (text->m_value.get ()))) + { + auto begin_url + = make_token<pp_token_begin_url> (label_text::take (url)); + auto end_url = make_token<pp_token_end_url> (); + insert_after (std::move (begin_url), begin_quote); + insert_after (std::move (end_url), text); + } + iter = end_quote->m_next; + } + else + iter = iter->m_next; + } +} + +void +pp_token_list::dump (FILE *out) const +{ + fprintf (out, "["); + for (auto iter = m_first; iter; iter = iter->m_next) + { + iter->dump (out); + if (iter->m_next) + fprintf (out, ", "); + } + fprintf (out, "]\n"); +} - location m_loc_last_open_quote; - std::vector<run> m_phase_3_quotes; -}; /* Adds a chunk to the end of formatted output, so that it will be printed by pp_output_formatted_text. */ void -chunk_info::append_formatted_chunk (const char *content) +chunk_info::append_formatted_chunk (obstack &s, const char *content) { unsigned int chunk_idx; for (chunk_idx = 0; m_args[chunk_idx]; chunk_idx++) ; - m_args[chunk_idx++] = content; + pp_token_list *tokens = pp_token_list::make (s); + tokens->push_back_text (label_text::borrow (content)); + m_args[chunk_idx++] = tokens; m_args[chunk_idx] = nullptr; } @@ -1261,34 +1490,33 @@ chunk_info::append_formatted_chunk (const char *content) void chunk_info::pop_from_output_buffer (output_buffer &buf) { - delete m_quotes; buf.cur_chunk_array = m_prev; obstack_free (&buf.chunk_obstack, this); } void -chunk_info::on_begin_quote (const output_buffer &buf, - unsigned chunk_idx, - const urlifier *urlifier) +chunk_info::dump (FILE *out) const { - if (!urlifier) - return; - if (!m_quotes) - m_quotes = new quoting_info (); - m_quotes->on_begin_quote (buf, chunk_idx); + for (size_t idx = 0; m_args[idx]; ++idx) + { + fprintf (out, "%i: ", (int)idx); + m_args[idx]->dump (out); + } } -void -chunk_info::on_end_quote (pretty_printer *pp, - output_buffer &buf, - unsigned chunk_idx, - const urlifier *urlifier) +/* Finish any text accumulating within CUR_OBSTACK, + terminating it. + Push a text pp_token to the end of TOK_LIST containing + a borrowed copy of the text in CUR_OBSTACK. */ + +static void +push_back_any_text (pp_token_list *tok_list, + obstack *cur_obstack) { - if (!urlifier) - return; - if (!m_quotes) - m_quotes = new quoting_info (); - m_quotes->on_end_quote (pp, buf, chunk_idx, *urlifier); + obstack_1grow (cur_obstack, '\0'); + tok_list->push_back_text + (label_text::borrow (XOBFINISH (cur_obstack, + const char *))); } /* The following format specifiers are recognized as being client independent: @@ -1338,36 +1566,22 @@ chunk_info::on_end_quote (pretty_printer *pp, /* Implementation of pp_format. Formatting phases 1 and 2: render TEXT->format_spec plus text->m_args_ptr into a series of chunks in pp_buffer (PP)->args[]. - Phase 3 is in pp_output_formatted_text. - - If URLIFIER is non-NULL, then use it to add URLs for quoted - strings, so that e.g. - "before %<quoted%> after" - with a URLIFIER that has a URL for "quoted" might be emitted as: - "before `BEGIN_URL(http://example.com)quotedEND_URL' after" - This is handled here for message fragments that are: - - quoted entirely in phase 1 (e.g. "%<this is quoted%>"), or - - quoted entirely in phase 2 (e.g. "%qs"), - Quoted fragments that use a mixture of both phases - (e.g. "%<this is a mixture: %s %>") - are stashed into the output_buffer's m_quotes for use in phase 3. */ + Phase 3 is in pp_output_formatted_text. */ void -pretty_printer::format (text_info *text, - const urlifier *urlifier) +pretty_printer::format (text_info *text) { output_buffer * const buffer = m_buffer; unsigned int chunk = 0, argno; - const char **formatters[PP_NL_ARGMAX]; + pp_token_list **formatters[PP_NL_ARGMAX]; /* Allocate a new chunk structure. */ chunk_info *new_chunk_array = XOBNEW (&buffer->chunk_obstack, chunk_info); new_chunk_array->m_prev = buffer->cur_chunk_array; - new_chunk_array->m_quotes = nullptr; buffer->cur_chunk_array = new_chunk_array; - const char **args = new_chunk_array->m_args; + pp_token_list **args = new_chunk_array->m_args; /* Formatting phase 1: split up TEXT->format_spec into chunks in pp_buffer (PP)->args[]. Even-numbered chunks are to be output @@ -1379,6 +1593,8 @@ pretty_printer::format (text_info *text, unsigned int curarg = 0; bool any_unnumbered = false, any_numbered = false; + pp_token_list *cur_token_list; + args[chunk++] = cur_token_list = pp_token_list::make (buffer->chunk_obstack); for (const char *p = text->m_format_spec; *p; ) { while (*p != '\0' && *p != '%') @@ -1402,44 +1618,39 @@ pretty_printer::format (text_info *text, case '<': { - obstack_grow (&buffer->chunk_obstack, - open_quote, strlen (open_quote)); - const char *colorstr = colorize_start (m_show_color, "quote"); - obstack_grow (&buffer->chunk_obstack, colorstr, strlen (colorstr)); + push_back_any_text (cur_token_list, &buffer->chunk_obstack); + cur_token_list->push_back<pp_token_begin_quote> (); p++; - - buffer->cur_chunk_array->on_begin_quote (*buffer, chunk, urlifier); continue; } case '>': { - buffer->cur_chunk_array->on_end_quote (this, *buffer, chunk, urlifier); - - const char *colorstr = colorize_stop (m_show_color); - obstack_grow (&buffer->chunk_obstack, colorstr, strlen (colorstr)); + push_back_any_text (cur_token_list, &buffer->chunk_obstack); + cur_token_list->push_back<pp_token_end_quote> (); + p++; + continue; } - /* FALLTHRU */ case '\'': - obstack_grow (&buffer->chunk_obstack, - close_quote, strlen (close_quote)); - p++; + { + push_back_any_text (cur_token_list, &buffer->chunk_obstack); + cur_token_list->push_back<pp_token_end_quote> (); + p++; + } continue; case '}': { - const char *endurlstr = get_end_url_string (this); - obstack_grow (&buffer->chunk_obstack, endurlstr, - strlen (endurlstr)); + push_back_any_text (cur_token_list, &buffer->chunk_obstack); + cur_token_list->push_back<pp_token_end_url> (); + p++; } - p++; continue; case 'R': { - const char *colorstr = colorize_stop (m_show_color); - obstack_grow (&buffer->chunk_obstack, colorstr, - strlen (colorstr)); + push_back_any_text (cur_token_list, &buffer->chunk_obstack); + cur_token_list->push_back<pp_token_end_color> (); p++; continue; } @@ -1454,11 +1665,14 @@ pretty_printer::format (text_info *text, default: /* Handled in phase 2. Terminate the plain chunk here. */ - obstack_1grow (&buffer->chunk_obstack, '\0'); - args[chunk++] = XOBFINISH (&buffer->chunk_obstack, const char *); + push_back_any_text (cur_token_list, &buffer->chunk_obstack); break; } + /* Start a new token list for the formatting args. */ + args[chunk] = cur_token_list + = pp_token_list::make (buffer->chunk_obstack); + if (ISDIGIT (*p)) { char *end; @@ -1478,7 +1692,7 @@ pretty_printer::format (text_info *text, } gcc_assert (argno < PP_NL_ARGMAX); gcc_assert (!formatters[argno]); - formatters[argno] = &args[chunk]; + formatters[argno] = &args[chunk++]; do { obstack_1grow (&buffer->chunk_obstack, *p); @@ -1530,17 +1744,24 @@ pretty_printer::format (text_info *text, } } if (*p == '\0') - break; + { + push_back_any_text (cur_token_list, &buffer->chunk_obstack); + break; + } obstack_1grow (&buffer->chunk_obstack, '\0'); + push_back_any_text (cur_token_list, &buffer->chunk_obstack); + + /* Start a new token list for the next (non-formatted) text. */ gcc_assert (chunk < PP_NL_ARGMAX * 2); - args[chunk++] = XOBFINISH (&buffer->chunk_obstack, const char *); + args[chunk++] = cur_token_list + = pp_token_list::make (buffer->chunk_obstack); } obstack_1grow (&buffer->chunk_obstack, '\0'); + push_back_any_text (cur_token_list, &buffer->chunk_obstack); gcc_assert (chunk < PP_NL_ARGMAX * 2); - args[chunk++] = XOBFINISH (&buffer->chunk_obstack, const char *); - args[chunk] = 0; + args[chunk] = nullptr; /* Set output to the argument obstack, and switch line-wrapping and prefixing off. */ @@ -1548,6 +1769,15 @@ pretty_printer::format (text_info *text, const int old_line_length = buffer->line_length; const pp_wrapping_mode_t old_wrapping_mode = pp_set_verbatim_wrapping (this); + /* Note that you can debug the state of the chunk arrays here using + (gdb) call buffer->cur_chunk_array->dump() + which, given e.g. "foo: %s bar: %s" might print: + 0: [TEXT("foo: ")] + 1: [TEXT("s")] + 2: [TEXT(" bar: ")] + 3: [TEXT("s")] + */ + /* Second phase. Replace each formatter with the formatted text it corresponds to. */ @@ -1561,10 +1791,20 @@ pretty_printer::format (text_info *text, const char *p; + /* We expect a single text token containing the formatter. */ + pp_token_list *tok_list = *(formatters[argno]); + gcc_assert (tok_list); + gcc_assert (tok_list->m_first == tok_list->m_end); + gcc_assert (tok_list->m_first->m_kind == pp_token::kind::text); + + /* Accumulate the value of the formatted text into here. */ + pp_token_list *formatted_tok_list + = pp_token_list::make (buffer->chunk_obstack); + /* We do not attempt to enforce any ordering on the modifier characters. */ - for (p = *formatters[argno];; p++) + for (p = as_a <pp_token_text *> (tok_list->m_first)->m_value.get ();; p++) { switch (*p) { @@ -1611,16 +1851,18 @@ pretty_printer::format (text_info *text, if (quote) { - pp_begin_quote (this, m_show_color); - buffer->cur_chunk_array->on_begin_quote (*buffer, chunk, urlifier); + push_back_any_text (formatted_tok_list, &buffer->chunk_obstack); + formatted_tok_list->push_back<pp_token_begin_quote> (); } switch (*p) { case 'r': - pp_string (this, colorize_start (m_show_color, - va_arg (*text->m_args_ptr, - const char *))); + { + const char *color = va_arg (*text->m_args_ptr, const char *); + formatted_tok_list->push_back<pp_token_begin_color> + (label_text::borrow (color)); + } break; case 'c': @@ -1752,17 +1994,16 @@ pretty_printer::format (text_info *text, diagnostic_event_id_ptr event_id = va_arg (*text->m_args_ptr, diagnostic_event_id_ptr); gcc_assert (event_id->known_p ()); - - pp_string (this, colorize_start (m_show_color, "path")); - pp_character (this, '('); - pp_decimal_int (this, event_id->one_based ()); - pp_character (this, ')'); - pp_string (this, colorize_stop (m_show_color)); + formatted_tok_list->push_back<pp_token_event_id> (*event_id); } break; case '{': - begin_url (va_arg (*text->m_args_ptr, const char *)); + { + const char *url = va_arg (*text->m_args_ptr, const char *); + formatted_tok_list->push_back<pp_token_begin_url> + (label_text::borrow (url)); + } break; case 'e': @@ -1771,7 +2012,7 @@ pretty_printer::format (text_info *text, = va_arg (*text->m_args_ptr, pp_element *); pp_markup::context ctxt (*this, *buffer, chunk, quote, /* by reference */ - urlifier); + formatted_tok_list); element->add_to_phase_2 (ctxt); } break; @@ -1786,22 +2027,23 @@ pretty_printer::format (text_info *text, (e.g. when printing "'TYPEDEF' aka 'TYPE'" in the C family of frontends). */ gcc_assert (pp_format_decoder (this)); + gcc_assert (formatted_tok_list); ok = m_format_decoder (this, text, p, precision, wide, plus, hash, "e, - formatters[argno]); + *formatted_tok_list); gcc_assert (ok); } } if (quote) { - buffer->cur_chunk_array->on_end_quote (this, *buffer, - chunk, urlifier); - pp_end_quote (this, m_show_color); + push_back_any_text (formatted_tok_list, &buffer->chunk_obstack); + formatted_tok_list->push_back<pp_token_end_quote> (); } - obstack_1grow (&buffer->chunk_obstack, '\0'); - *formatters[argno] = XOBFINISH (&buffer->chunk_obstack, const char *); + push_back_any_text (formatted_tok_list, &buffer->chunk_obstack); + delete *formatters[argno]; + *formatters[argno] = formatted_tok_list; } if (CHECKING_P) @@ -1832,6 +2074,8 @@ struct auto_obstack obstack_free (&m_obstack, NULL); } + operator obstack & () { return m_obstack; } + void grow (const void *src, size_t length) { obstack_grow (&m_obstack, src, length); @@ -1850,130 +2094,117 @@ struct auto_obstack obstack m_obstack; }; -/* Subroutine of pp_output_formatted_text for the awkward case where - quoted text straddles multiple chunks. - - Flush PP's buffer's chunks to PP's output buffer, whilst inserting - URLs for any quoted text that should be URLified. - - For example, given: - | pp_format (pp, - | "unrecognized option %qs; did you mean %<-%s%>", - | "foo", "foption"); - we would have these chunks: - | chunk 0: "unrecognized option " - | chunk 1: "`foo'" (already checked for urlification) - | chunk 2: "; did you mean `-" - | ^* - | chunk 3: "foption" - | ******* - | chunk 4: "'" - | ^ - and this quoting_info would have recorded the open quote near the end - of chunk 2 and close quote at the start of chunk 4; this function would - check the combination of the end of chunk 2 and all of chunk 3 ("-foption") - for urlification. */ +/* Format of a message pointed to by TEXT. + If URLIFIER is non-null then use it on any quoted text that was not + handled in phases 1 or 2 to potentially add URLs. */ void -quoting_info::handle_phase_3 (pretty_printer *pp, - const urlifier &urlifier) +pp_output_formatted_text (pretty_printer *pp, + const urlifier *urlifier) { - unsigned int chunk; output_buffer * const buffer = pp_buffer (pp); + gcc_assert (buffer->obstack == &buffer->formatted_obstack); + chunk_info *chunk_array = buffer->cur_chunk_array; - const char * const *args = chunk_array->get_args (); - quoting_info *quoting = chunk_array->get_quoting_info (); - - /* We need to construct the string into an intermediate buffer - for this case, since using pp_string can introduce prefixes - and line-wrapping, and omit whitespace at the start of lines. */ - auto_obstack combined_buf; - - /* Iterate simultaneously through both - - the chunks and - - the runs of quoted characters - Accumulate text from the chunks into combined_buf, and handle - runs of quoted characters when handling the chunks they - correspond to. */ - size_t start_of_run_byte_offset = 0; - std::vector<quoting_info::run>::const_iterator iter_run - = quoting->m_phase_3_quotes.begin (); - std::vector<quoting_info::run>::const_iterator end_runs - = quoting->m_phase_3_quotes.end (); - for (chunk = 0; args[chunk]; chunk++) - { - size_t start_of_chunk_idx = combined_buf.object_size (); + pp_token_list * const *token_lists = chunk_array->get_token_lists (); - combined_buf.grow (args[chunk], strlen (args[chunk])); + { + /* Consolidate into one token list. */ + pp_token_list tokens (buffer->chunk_obstack); + for (unsigned chunk = 0; token_lists[chunk]; chunk++) + { + tokens.push_back_list (std::move (*token_lists[chunk])); + delete token_lists[chunk]; + } - if (iter_run != end_runs - && chunk == iter_run->m_end.m_chunk_idx) - { - /* A run is ending; consider for it urlification. */ - const size_t end_of_run_byte_offset - = start_of_chunk_idx + iter_run->m_end.m_byte_offset; - const size_t end_offset - = urlify_quoted_string (pp, - &combined_buf.m_obstack, - &urlifier, - start_of_run_byte_offset, - end_of_run_byte_offset); + tokens.replace_custom_tokens (); - /* If URLification occurred it will have grown the buffer. - We need to update start_of_chunk_idx so that offsets - relative to it are still correct, for the case where - we have a chunk that both ends a quoted run and starts - another quoted run. */ - gcc_assert (end_offset >= end_of_run_byte_offset); - start_of_chunk_idx += end_offset - end_of_run_byte_offset; + tokens.merge_consecutive_text_tokens (); - iter_run++; - } - if (iter_run != end_runs - && chunk == iter_run->m_start.m_chunk_idx) - { - /* Note where the run starts w.r.t. the composed buffer. */ - start_of_run_byte_offset - = start_of_chunk_idx + iter_run->m_start.m_byte_offset; - } - } + if (urlifier) + tokens.apply_urlifier (*urlifier); - /* Now print to PP. */ - const char *start - = static_cast <const char *> (combined_buf.object_base ()); - pp_maybe_wrap_text (pp, start, start + combined_buf.object_size ()); + /* This is a third phase, first 2 phases done in pp_format_args. + Now we actually print it. */ + if (pp->m_token_printer) + pp->m_token_printer->print_tokens (pp, tokens); + else + default_token_printer (pp, tokens); + + /* Close the scope here to ensure that "tokens" above is fully cleared up + before popping the current chunk_info, since that latter will pop + the chunk_obstack, and "tokens" may be using blocks within + the current chunk_info's chunk_obstack level. */ + } + + chunk_array->pop_from_output_buffer (*buffer); } -/* Format of a message pointed to by TEXT. - If URLIFIER is non-null then use it on any quoted text that was not - handled in phases 1 or 2 to potentially add URLs. */ +/* Default implementation of token printing. */ -void -pp_output_formatted_text (pretty_printer *pp, - const urlifier *urlifier) +static void +default_token_printer (pretty_printer *pp, + const pp_token_list &tokens) { - unsigned int chunk; - output_buffer * const buffer = pp_buffer (pp); - chunk_info *chunk_array = buffer->cur_chunk_array; - const char * const *args = chunk_array->get_args (); - quoting_info *quoting = chunk_array->get_quoting_info (); + /* Convert to text, possibly with colorization, URLs, etc. */ + for (auto iter = tokens.m_first; iter; iter = iter->m_next) + switch (iter->m_kind) + { + default: + gcc_unreachable (); - gcc_assert (buffer->obstack == &buffer->formatted_obstack); + case pp_token::kind::text: + { + pp_token_text *sub = as_a <pp_token_text *> (iter); + pp_string (pp, sub->m_value.get ()); + } + break; - /* This is a third phase, first 2 phases done in pp_format_args. - Now we actually print it. */ + case pp_token::kind::begin_color: + { + pp_token_begin_color *sub = as_a <pp_token_begin_color *> (iter); + pp_string (pp, colorize_start (pp_show_color (pp), + sub->m_value.get ())); + } + break; + case pp_token::kind::end_color: + pp_string (pp, colorize_stop (pp_show_color (pp))); + break; - /* If we have any deferred urlification, handle it now. */ - if (urlifier - && pp->supports_urls_p () - && quoting - && quoting->has_phase_3_quotes_p ()) - quoting->handle_phase_3 (pp, *urlifier); - else - for (chunk = 0; args[chunk]; chunk++) - pp_string (pp, args[chunk]); + 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; - chunk_array->pop_from_output_buffer (*buffer); + case pp_token::kind::begin_url: + { + pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter); + pp_begin_url (pp, sub->m_value.get ()); + } + break; + case pp_token::kind::end_url: + pp_end_url (pp); + 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 ()); + pp_string (pp, colorize_start (pp_show_color (pp), "path")); + pp_character (pp, '('); + pp_decimal_int (pp, sub->m_event_id.one_based ()); + pp_character (pp, ')'); + pp_string (pp, colorize_stop (pp_show_color (pp))); + } + break; + + case pp_token::kind::custom_data: + /* These should have been eliminated by replace_custom_tokens. */ + gcc_unreachable (); + break; + } } /* Helper subroutine of output_verbatim and verbatim. Do the appropriate @@ -2112,6 +2343,7 @@ pretty_printer::pretty_printer (int maximum_length) m_wrapping (), m_format_decoder (nullptr), m_format_postprocessor (NULL), + m_token_printer (nullptr), m_emitted_prefix (false), m_need_newline (false), m_translate_identifiers (true), @@ -2137,6 +2369,7 @@ pretty_printer::pretty_printer (const pretty_printer &other) m_wrapping (other.m_wrapping), m_format_decoder (other.m_format_decoder), m_format_postprocessor (NULL), + m_token_printer (other.m_token_printer), m_emitted_prefix (other.m_emitted_prefix), m_need_newline (other.m_need_newline), m_translate_identifiers (other.m_translate_identifiers), @@ -2742,8 +2975,9 @@ void pp_markup::context::begin_quote () { gcc_assert (!m_quoted); - pp_begin_quote (&m_pp, pp_show_color (&m_pp)); - m_buf.cur_chunk_array->on_begin_quote (m_buf, m_chunk_idx, m_urlifier); + gcc_assert (m_formatted_token_list); + push_back_any_text (); + m_formatted_token_list->push_back<pp_token_begin_quote> (); m_quoted = true; } @@ -2754,8 +2988,9 @@ pp_markup::context::end_quote () printing a type emitting "TYPEDEF' {aka `TYPE'}". */ if (!m_quoted) return; - m_buf.cur_chunk_array->on_end_quote (&m_pp, m_buf, m_chunk_idx, m_urlifier); - pp_end_quote (&m_pp, pp_show_color (&m_pp)); + gcc_assert (m_formatted_token_list); + push_back_any_text (); + m_formatted_token_list->push_back<pp_token_end_quote> (); m_quoted = false; } @@ -2764,7 +2999,10 @@ pp_markup::context::begin_highlight_color (const char *color_name) { if (!pp_show_highlight_colors (&m_pp)) return; - pp_string (&m_pp, colorize_start (pp_show_color (&m_pp), color_name)); + + push_back_any_text (); + m_formatted_token_list->push_back <pp_token_begin_color> + (label_text::borrow (color_name)); } void @@ -2772,10 +3010,20 @@ pp_markup::context::end_highlight_color () { if (!pp_show_highlight_colors (&m_pp)) return; - const char *colorstr = colorize_stop (pp_show_color (&m_pp)); - obstack_grow (&m_buf.chunk_obstack, colorstr, strlen (colorstr)); + + push_back_any_text (); + m_formatted_token_list->push_back<pp_token_end_color> (); } +void +pp_markup::context::push_back_any_text () +{ + obstack *cur_obstack = m_buf.obstack; + obstack_1grow (cur_obstack, '\0'); + m_formatted_token_list->push_back_text + (label_text::borrow (XOBFINISH (cur_obstack, + const char *))); +} /* Color names for expressing "expected" vs "actual" values. */ const char *const highlight_colors::expected = "highlight-a"; @@ -3038,6 +3286,245 @@ test_pp_format () 1776, "second"); } +static void +test_merge_consecutive_text_tokens () +{ + auto_obstack s; + pp_token_list list (s); + list.push_back_text (label_text::borrow ("hello")); + list.push_back_text (label_text::borrow (" ")); + list.push_back_text (label_text::take (xstrdup ("world"))); + list.push_back_text (label_text::borrow ("!")); + + list.merge_consecutive_text_tokens (); + // We expect a single text token, with concatenated text + ASSERT_EQ (list.m_first, list.m_end); + pp_token *tok = list.m_first; + ASSERT_NE (tok, nullptr); + ASSERT_EQ (tok->m_kind, pp_token::kind::text); + ASSERT_STREQ (as_a <pp_token_text *> (tok)->m_value.get (), "hello world!"); +} + +/* Verify that we can create custom tokens that can be lowered + in phase 3. */ + +static void +test_custom_tokens_1 () +{ + struct custom_token_adder : public pp_element + { + public: + struct value : public pp_token_custom_data::value + { + value (custom_token_adder &adder) + : m_adder (adder) + { + m_adder.m_num_living_values++; + } + value (const value &other) + : m_adder (other.m_adder) + { + m_adder.m_num_living_values++; + } + value (value &&other) + : m_adder (other.m_adder) + { + m_adder.m_num_living_values++; + } + value &operator= (const value &other) = delete; + value &operator= (value &&other) = delete; + ~value () + { + m_adder.m_num_living_values--; + } + + void dump (FILE *out) const final override + { + fprintf (out, "\"%s\"", m_adder.m_name); + } + + bool as_standard_tokens (pp_token_list &out) final override + { + ASSERT_TRUE (m_adder.m_num_living_values > 0); + out.push_back<pp_token_text> (label_text::borrow (m_adder.m_name)); + return true; + } + + custom_token_adder &m_adder; + }; + + custom_token_adder (const char *name) + : m_name (name), + m_num_living_values (0) + { + } + + void add_to_phase_2 (pp_markup::context &ctxt) final override + { + auto val_ptr = make_unique<value> (*this); + ctxt.m_formatted_token_list->push_back<pp_token_custom_data> + (std::move (val_ptr)); + } + + const char *m_name; + int m_num_living_values; + }; + + custom_token_adder e1 ("foo"); + custom_token_adder e2 ("bar"); + ASSERT_EQ (e1.m_num_living_values, 0); + ASSERT_EQ (e2.m_num_living_values, 0); + + pretty_printer pp; + pp_printf (&pp, "before %e middle %e after", &e1, &e2); + + /* Verify that instances were cleaned up. */ + ASSERT_EQ (e1.m_num_living_values, 0); + ASSERT_EQ (e2.m_num_living_values, 0); + + ASSERT_STREQ (pp_formatted_text (&pp), + "before foo middle bar after"); +} + +/* Verify that we can create custom tokens that aren't lowered + in phase 3, but instead are handled by a custom token_printer. + Use this to verify the inputs seen by such token_printers. */ + +static void +test_custom_tokens_2 () +{ + struct custom_token_adder : public pp_element + { + struct value : public pp_token_custom_data::value + { + public: + value (custom_token_adder &adder) + : m_adder (adder) + { + m_adder.m_num_living_values++; + } + value (const value &other) + : m_adder (other.m_adder) + { + m_adder.m_num_living_values++; + } + value (value &&other) + : m_adder (other.m_adder) + { + m_adder.m_num_living_values++; + } + value &operator= (const value &other) = delete; + value &operator= (value &&other) = delete; + ~value () + { + m_adder.m_num_living_values--; + } + + void dump (FILE *out) const final override + { + fprintf (out, "\"%s\"", m_adder.m_name); + } + + bool as_standard_tokens (pp_token_list &) final override + { + return false; + } + + custom_token_adder &m_adder; + }; + + custom_token_adder (const char *name) + : m_name (name), + m_num_living_values (0) + { + } + + void add_to_phase_2 (pp_markup::context &ctxt) final override + { + auto val_ptr = make_unique<value> (*this); + ctxt.m_formatted_token_list->push_back<pp_token_custom_data> + (std::move (val_ptr)); + } + + const char *m_name; + int m_num_living_values; + }; + + class custom_token_printer : public token_printer + { + void print_tokens (pretty_printer *pp, + const pp_token_list &tokens) final override + { + /* Verify that TOKENS has: + [TEXT("before "), CUSTOM("foo"), TEXT(" middle "), CUSTOM("bar"), + TEXT(" after")] */ + pp_token *tok_0 = tokens.m_first; + ASSERT_NE (tok_0, nullptr); + ASSERT_EQ (tok_0->m_kind, pp_token::kind::text); + ASSERT_STREQ (as_a<pp_token_text *> (tok_0)->m_value.get (), + "before "); + + pp_token *tok_1 = tok_0->m_next; + ASSERT_NE (tok_1, nullptr); + ASSERT_EQ (tok_1->m_prev, tok_0); + ASSERT_EQ (tok_1->m_kind, pp_token::kind::custom_data); + + custom_token_adder::value *v1 + = static_cast <custom_token_adder::value *> + (as_a<pp_token_custom_data *> (tok_1)->m_value.get ()); + ASSERT_STREQ (v1->m_adder.m_name, "foo"); + ASSERT_TRUE (v1->m_adder.m_num_living_values > 0); + + pp_token *tok_2 = tok_1->m_next; + ASSERT_NE (tok_2, nullptr); + ASSERT_EQ (tok_2->m_prev, tok_1); + ASSERT_EQ (tok_2->m_kind, pp_token::kind::text); + ASSERT_STREQ (as_a<pp_token_text *> (tok_2)->m_value.get (), + " middle "); + + pp_token *tok_3 = tok_2->m_next; + ASSERT_NE (tok_3, nullptr); + ASSERT_EQ (tok_3->m_prev, tok_2); + ASSERT_EQ (tok_3->m_kind, pp_token::kind::custom_data); + custom_token_adder::value *v3 + = static_cast <custom_token_adder::value *> + (as_a<pp_token_custom_data *> (tok_3)->m_value.get ()); + ASSERT_STREQ (v3->m_adder.m_name, "bar"); + ASSERT_TRUE (v3->m_adder.m_num_living_values > 0); + + pp_token *tok_4 = tok_3->m_next; + ASSERT_NE (tok_4, nullptr); + ASSERT_EQ (tok_4->m_prev, tok_3); + ASSERT_EQ (tok_4->m_kind, pp_token::kind::text); + ASSERT_STREQ (as_a<pp_token_text *> (tok_4)->m_value.get (), + " after"); + ASSERT_EQ (tok_4->m_next, nullptr); + + /* Normally we'd loop over the tokens, printing them to PP + and handling the custom tokens. + Instead, print a message to PP to verify that we were called. */ + pp_string (pp, "print_tokens was called"); + } + }; + + custom_token_adder e1 ("foo"); + custom_token_adder e2 ("bar"); + ASSERT_EQ (e1.m_num_living_values, 0); + ASSERT_EQ (e2.m_num_living_values, 0); + + custom_token_printer tp; + pretty_printer pp; + pp.set_token_printer (&tp); + pp_printf (&pp, "before %e middle %e after", &e1, &e2); + + /* Verify that instances were cleaned up. */ + ASSERT_EQ (e1.m_num_living_values, 0); + ASSERT_EQ (e2.m_num_living_values, 0); + + ASSERT_STREQ (pp_formatted_text (&pp), + "print_tokens was called"); +} + /* A subclass of pretty_printer for use by test_prefixes_and_wrapping. */ class test_pretty_printer : public pretty_printer @@ -3247,7 +3734,7 @@ pp_printf_with_urlifier (pretty_printer *pp, va_start (ap, msg); text_info text (msg, &ap, errno); - pp_format (pp, &text, urlifier); + pp_format (pp, &text); pp_output_formatted_text (pp, urlifier); va_end (ap); } @@ -3403,6 +3890,18 @@ test_urlification () ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar", pp_formatted_text (&pp)); } + + /* Test the example from pretty-print-format-impl.h. */ + { + pretty_printer pp; + pp.set_url_format (URL_FORMAT_ST); + pp_printf_with_urlifier (&pp, &urlifier, + "foo: %i, bar: %s, option: %qs", + 42, "baz", "-foption"); + ASSERT_STREQ (pp_formatted_text (&pp), + "foo: 42, bar: baz, option:" + " `]8;;http://example.com\\-foption]8;;\\'"); + } } /* Test multibyte awareness. */ @@ -3452,6 +3951,9 @@ pretty_print_cc_tests () { test_basic_printing (); test_pp_format (); + test_merge_consecutive_text_tokens (); + test_custom_tokens_1 (); + test_custom_tokens_2 (); test_prefixes_and_wrapping (); test_urls (); test_urls_from_braces (); diff --git a/gcc/pretty-print.h b/gcc/pretty-print.h index d28814a..e0505b2 100644 --- a/gcc/pretty-print.h +++ b/gcc/pretty-print.h @@ -69,58 +69,15 @@ enum diagnostic_prefixing_rule_t DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE = 0x2 }; -class quoting_info; +class chunk_info; class output_buffer; +class pp_token_list; class urlifier; namespace pp_markup { class context; } // namespace pp_markup -/* The chunk_info data structure forms a stack of the results from the - first phase of formatting (pp_format) which have not yet been - output (pp_output_formatted_text). A stack is necessary because - the diagnostic starter may decide to generate its own output by way - of the formatter. */ -class chunk_info -{ - friend class pretty_printer; - friend class pp_markup::context; - -public: - const char * const *get_args () const { return m_args; } - quoting_info *get_quoting_info () const { return m_quotes; } - - void append_formatted_chunk (const char *content); - - void pop_from_output_buffer (output_buffer &buf); - -private: - void on_begin_quote (const output_buffer &buf, - unsigned chunk_idx, - const urlifier *urlifier); - - void on_end_quote (pretty_printer *pp, - output_buffer &buf, - unsigned chunk_idx, - const urlifier *urlifier); - - /* Pointer to previous chunk on the stack. */ - chunk_info *m_prev; - - /* Array of chunks to output. Each chunk is a NUL-terminated string. - In the first phase of formatting, even-numbered chunks are - to be output verbatim, odd-numbered chunks are format specifiers. - The second phase replaces all odd-numbered chunks with formatted - text, and the third phase simply emits all the chunks in sequence - with appropriate line-wrapping. */ - const char *m_args[PP_NL_ARGMAX * 2]; - - /* If non-null, information on quoted text runs within the chunks - for use by a urlifier. */ - quoting_info *m_quotes; -}; - /* The output buffer datatype. This is best seen as an abstract datatype whose fields should not be accessed directly by clients. */ class output_buffer @@ -220,7 +177,7 @@ struct pp_wrapping_mode_t A client-supplied formatter returns true if everything goes well, otherwise it returns false. */ typedef bool (*printer_fn) (pretty_printer *, text_info *, const char *, - int, bool, bool, bool, bool *, const char **); + int, bool, bool, bool, bool *, pp_token_list &); /* Base class for an optional client-supplied object for doing additional processing between stages 2 and 3 of formatted printing. */ @@ -232,6 +189,18 @@ class format_postprocessor virtual void handle (pretty_printer *) = 0; }; +/* Abstract base class for writing formatted tokens to the pretty_printer's + text buffer, allowing for output formats and dumpfiles to override + how different kinds of tokens are handled. */ + +class token_printer +{ +public: + virtual ~token_printer () {} + virtual void print_tokens (pretty_printer *pp, + const pp_token_list &tokens) = 0; +}; + inline bool & pp_needs_newline (pretty_printer *pp); /* True if PRETTY-PRINTER is in line-wrapping mode. */ @@ -279,6 +248,9 @@ public: friend format_postprocessor *& pp_format_postprocessor (pretty_printer *pp); friend bool & pp_show_highlight_colors (pretty_printer *pp); + friend void pp_output_formatted_text (pretty_printer *, + const urlifier *); + /* Default construct a pretty printer with specified maximum line length cut off limit. */ explicit pretty_printer (int = 0); @@ -293,12 +265,16 @@ public: m_buffer->stream = outfile; } + void set_token_printer (token_printer* tp) + { + m_token_printer = tp; // borrowed + } + void set_prefix (char *prefix); void emit_prefix (); - void format (text_info *text, - const urlifier *urlifier); + void format (text_info *text); void maybe_space (); @@ -357,8 +333,9 @@ private: If the BUFFER needs additional characters from the format string, it should advance the TEXT->format_spec as it goes. When FORMAT_DECODER returns, TEXT->format_spec should point to the last character processed. - The QUOTE and BUFFER_PTR are passed in, to allow for deferring-handling - of format codes (e.g. %H and %I in the C++ frontend). */ + The QUOTE and FORMATTED_TOKEN_LIST are passed in, to allow for + deferring-handling of format codes (e.g. %H and %I in + the C++ frontend). */ printer_fn m_format_decoder; /* If non-NULL, this is called by pp_format once after all format codes @@ -367,6 +344,12 @@ private: format codes (which interract with each other). */ format_postprocessor *m_format_postprocessor; + /* This is used by pp_output_formatted_text after it has converted all + formatted chunks into a single list of tokens. + Can be nullptr. + Borrowed from the output format or from dump_pretty_printer. */ + token_printer *m_token_printer; + /* Nonzero if current PREFIX was emitted at least once. */ bool m_emitted_prefix; @@ -586,10 +569,9 @@ extern void pp_verbatim (pretty_printer *, const char *, ...) ATTRIBUTE_GCC_PPDIAG(2,3); extern void pp_flush (pretty_printer *); extern void pp_really_flush (pretty_printer *); -inline void pp_format (pretty_printer *pp, text_info *text, - const urlifier *urlifier = nullptr) +inline void pp_format (pretty_printer *pp, text_info *text) { - pp->format (text, urlifier); + pp->format (text); } extern void pp_output_formatted_text (pretty_printer *, const urlifier * = nullptr); diff --git a/gcc/sese.cc b/gcc/sese.cc index e5c4605..e9b17fa 100644 --- a/gcc/sese.cc +++ b/gcc/sese.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc index 793932a..dc040df 100644 --- a/gcc/targhooks.cc +++ b/gcc/targhooks.cc @@ -47,6 +47,7 @@ along with GCC; see the file COPYING3. If not see comment can thus be removed at that point. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "target.h" diff --git a/gcc/testsuite/gcc.dg/plugin/dump_plugin.c b/gcc/testsuite/gcc.dg/plugin/dump_plugin.c index 12573d6..fc76d69 100644 --- a/gcc/testsuite/gcc.dg/plugin/dump_plugin.c +++ b/gcc/testsuite/gcc.dg/plugin/dump_plugin.c @@ -1,5 +1,6 @@ /* Plugin for testing dumpfile.c. */ +#define INCLUDE_MEMORY #include "gcc-plugin.h" #include "config.h" #include "system.h" diff --git a/gcc/testsuite/gcc.dg/sarif-output/bad-pragma.c b/gcc/testsuite/gcc.dg/sarif-output/bad-pragma.c new file mode 100644 index 0000000..db274de --- /dev/null +++ b/gcc/testsuite/gcc.dg/sarif-output/bad-pragma.c @@ -0,0 +1,16 @@ +/* Verify that SARIF output can capture URLs in diagnostics + related to a bad pragma. */ + +/* { dg-do compile } */ +/* { dg-options "-fdiagnostics-format=sarif-file -Wpragmas" } */ + +#pragma GCC diagnostic ignored "-Wmisleading-indenttion" + +int nonempty; + +/* Verify that some JSON was written to a file with the expected name: + { dg-final { verify-sarif-file } } */ + +/* Use a Python script to verify various properties about the generated + .sarif file: + { dg-final { run-sarif-pytest bad-pragma.c "test-bad-pragma.py" } } */ diff --git a/gcc/testsuite/gcc.dg/sarif-output/test-bad-pragma.py b/gcc/testsuite/gcc.dg/sarif-output/test-bad-pragma.py new file mode 100644 index 0000000..140bb33 --- /dev/null +++ b/gcc/testsuite/gcc.dg/sarif-output/test-bad-pragma.py @@ -0,0 +1,38 @@ +from sarif import * + +import pytest + +@pytest.fixture(scope='function', autouse=True) +def sarif(): + return sarif_from_env() + +def test_messages_have_embedded_urls(sarif): + runs = sarif['runs'] + run = runs[0] + results = run['results'] + + # We expect a single warning with a secondary location. + # + # The textual form of the diagnostic would look like this: + # . PATH/bad-pragma.c:7:32: warning: unknown option after '#pragma GCC diagnostic' kind [-Wpragmas] + # . 7 | #pragma GCC diagnostic ignored "-Wmisleading-indenttion" + # . | ^~~~~~~~~~~~~~~~~~~~~~~~~ + # . PATH/bad-pragma.c:7:32: note: did you mean '-Wmisleading-indentation'? + assert len(results) == 1 + + result = results[0] + assert result['ruleId'] == '-Wpragmas' + assert result['level'] == 'warning' + assert result['message']['text'] \ + == "unknown option after '[#pragma GCC diagnostic](https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Pragmas.html)' kind" + # Note how we expect an embedded link in the above for the docs for #pragma GCC diagnostic + + # We expect one related location, for the note. + relatedLocations = result['relatedLocations'] + assert len(relatedLocations) == 1 + + rel_loc = relatedLocations[0] + assert rel_loc['message']['text'] \ + == "did you mean '[-Wmisleading-indentation](https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html#index-Wmisleading-indentation)'?" + # Again, we expect an embedded link in the above, this time to the + # docs for the suggested option diff --git a/gcc/testsuite/gcc.dg/sarif-output/test-include-chain-2.py b/gcc/testsuite/gcc.dg/sarif-output/test-include-chain-2.py index 761fe1b..843f89a 100644 --- a/gcc/testsuite/gcc.dg/sarif-output/test-include-chain-2.py +++ b/gcc/testsuite/gcc.dg/sarif-output/test-include-chain-2.py @@ -96,9 +96,11 @@ def test_location_relationships(sarif): == " __builtin_free (ptr); // 1st\n" assert threadFlow['locations'][0]['kinds'] == ['release', 'memory'] assert threadFlow['locations'][0]['executionOrder'] == 1 - + + # We should have an embedded link in this event's message to the + # other event's location within the SARIF file: assert threadFlow['locations'][1]['location']['message']['text'] \ - == "second 'free' here; first 'free' was at (1)" + == "second 'free' here; first 'free' was at [(1)](sarif:/runs/0/results/0/codeFlows/0/threadFlows/0/locations/0)" assert threadFlow['locations'][1]['location']['physicalLocation']['contextRegion']['snippet']['text'] \ == " __builtin_free (ptr); // 2nd\n" assert threadFlow['locations'][1]['kinds'] == ['danger'] diff --git a/gcc/testsuite/gfortran.dg/use_rename_12.f90 b/gcc/testsuite/gfortran.dg/use_rename_12.f90 new file mode 100644 index 0000000..0447d5f --- /dev/null +++ b/gcc/testsuite/gfortran.dg/use_rename_12.f90 @@ -0,0 +1,27 @@ +! { dg-do compile } +! PR fortran/116530 - ICE with member of namelist renamed by use module +! +! Reported by philippe.wautelet at cnrs.fr + +module mod_nml1 + implicit none + logical :: ldiag + namelist /nam_nml1/ldiag +end module mod_nml1 + +module mod_interm + use mod_nml1 +end module mod_interm + +program ice_nml + use mod_nml1, ldiag_nml1 => ldiag + use mod_nml1, only : ldiag_only => ldiag + use mod_interm + implicit none + integer :: ilu = 10 + read(unit=ilu,nml=nam_nml1) + write(unit=*,nml=nam_nml1) + print *, ldiag + print *, ldiag_nml1 + print *, ldiag_only +end program ice_nml diff --git a/gcc/tree-data-ref.cc b/gcc/tree-data-ref.cc index bd61069..48798f4 100644 --- a/gcc/tree-data-ref.cc +++ b/gcc/tree-data-ref.cc @@ -74,6 +74,7 @@ along with GCC; see the file COPYING3. If not see */ #define INCLUDE_ALGORITHM +#define INCLUDE_MEMORY #include "config.h" #include "system.h" #include "coretypes.h" diff --git a/gcc/tree-diagnostic.cc b/gcc/tree-diagnostic.cc index fc78231..466725f 100644 --- a/gcc/tree-diagnostic.cc +++ b/gcc/tree-diagnostic.cc @@ -55,7 +55,7 @@ default_tree_diagnostic_starter (diagnostic_context *context, bool default_tree_printer (pretty_printer *pp, text_info *text, const char *spec, int precision, bool wide, bool set_locus, bool hash, - bool *, const char **) + bool *, pp_token_list &) { tree t; diff --git a/gcc/tree-diagnostic.h b/gcc/tree-diagnostic.h index 6ebac38..98ca654 100644 --- a/gcc/tree-diagnostic.h +++ b/gcc/tree-diagnostic.h @@ -53,6 +53,6 @@ void diagnostic_report_current_function (diagnostic_context *, void tree_diagnostics_defaults (diagnostic_context *context); bool default_tree_printer (pretty_printer *, text_info *, const char *, - int, bool, bool, bool, bool *, const char **); + int, bool, bool, bool, bool *, pp_token_list &); #endif /* ! GCC_TREE_DIAGNOSTIC_H */ diff --git a/gcc/tree-if-conv.cc b/gcc/tree-if-conv.cc index 735e477..0346a13 100644 --- a/gcc/tree-if-conv.cc +++ b/gcc/tree-if-conv.cc @@ -81,6 +81,7 @@ along with GCC; see the file COPYING3. If not see */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-loop-distribution.cc b/gcc/tree-loop-distribution.cc index 10f261a..f0430ed 100644 --- a/gcc/tree-loop-distribution.cc +++ b/gcc/tree-loop-distribution.cc @@ -90,6 +90,7 @@ along with GCC; see the file COPYING3. If not see data reuse. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-parloops.cc b/gcc/tree-parloops.cc index 888a834f..f446865 100644 --- a/gcc/tree-parloops.cc +++ b/gcc/tree-parloops.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-predcom.cc b/gcc/tree-predcom.cc index 9844fee..eed878b 100644 --- a/gcc/tree-predcom.cc +++ b/gcc/tree-predcom.cc @@ -205,6 +205,7 @@ along with GCC; see the file COPYING3. If not see i * i with ii_last + 2 * i + 1), to generalize strength reduction. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-ssa-live.cc b/gcc/tree-ssa-live.cc index 60dfc05..0739faa 100644 --- a/gcc/tree-ssa-live.cc +++ b/gcc/tree-ssa-live.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-ssa-loop-ivcanon.cc b/gcc/tree-ssa-loop-ivcanon.cc index 5ef24a9..a8d25ad 100644 --- a/gcc/tree-ssa-loop-ivcanon.cc +++ b/gcc/tree-ssa-loop-ivcanon.cc @@ -36,6 +36,7 @@ along with GCC; see the file COPYING3. If not see info). */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-ssa-loop-ivopts.cc b/gcc/tree-ssa-loop-ivopts.cc index c3218a3..dfe1b254 100644 --- a/gcc/tree-ssa-loop-ivopts.cc +++ b/gcc/tree-ssa-loop-ivopts.cc @@ -90,6 +90,7 @@ along with GCC; see the file COPYING3. If not see profitable. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-ssa-loop-prefetch.cc b/gcc/tree-ssa-loop-prefetch.cc index bb5d5de..52ea3ba 100644 --- a/gcc/tree-ssa-loop-prefetch.cc +++ b/gcc/tree-ssa-loop-prefetch.cc @@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-ssa-loop-unswitch.cc b/gcc/tree-ssa-loop-unswitch.cc index 14b0df1..7601d91 100644 --- a/gcc/tree-ssa-loop-unswitch.cc +++ b/gcc/tree-ssa-loop-unswitch.cc @@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-ssa-phiopt.cc b/gcc/tree-ssa-phiopt.cc index 95bac33..9a009e1 100644 --- a/gcc/tree-ssa-phiopt.cc +++ b/gcc/tree-ssa-phiopt.cc @@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-ssa-threadbackward.cc b/gcc/tree-ssa-threadbackward.cc index ea8d7b8..4bc72ec 100644 --- a/gcc/tree-ssa-threadbackward.cc +++ b/gcc/tree-ssa-threadbackward.cc @@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-ssa-threadupdate.cc b/gcc/tree-ssa-threadupdate.cc index fa61ba9..c88cc1d 100644 --- a/gcc/tree-ssa-threadupdate.cc +++ b/gcc/tree-ssa-threadupdate.cc @@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-vect-data-refs.cc b/gcc/tree-vect-data-refs.cc index 5b0d548..fe7fdec 100644 --- a/gcc/tree-vect-data-refs.cc +++ b/gcc/tree-vect-data-refs.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-vect-generic.cc b/gcc/tree-vect-generic.cc index 4bcab71..3041fb8 100644 --- a/gcc/tree-vect-generic.cc +++ b/gcc/tree-vect-generic.cc @@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-vect-loop-manip.cc b/gcc/tree-vect-loop-manip.cc index 57dbcbe..cb7843f 100644 --- a/gcc/tree-vect-loop-manip.cc +++ b/gcc/tree-vect-loop-manip.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc index 6456220..1fb7bbd 100644 --- a/gcc/tree-vect-loop.cc +++ b/gcc/tree-vect-loop.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #define INCLUDE_ALGORITHM +#define INCLUDE_MEMORY #include "config.h" #include "system.h" #include "coretypes.h" diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc index 9158af9..3162250 100644 --- a/gcc/tree-vect-patterns.cc +++ b/gcc/tree-vect-patterns.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-vect-slp-patterns.cc b/gcc/tree-vect-slp-patterns.cc index 4a582ec..8adae8a 100644 --- a/gcc/tree-vect-slp-patterns.cc +++ b/gcc/tree-vect-slp-patterns.cc @@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc index fe49811..cfdf59a 100644 --- a/gcc/tree-vect-slp.cc +++ b/gcc/tree-vect-slp.cc @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #include "config.h" #define INCLUDE_ALGORITHM +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc index 9eb73a5..03c65f0 100644 --- a/gcc/tree-vect-stmts.cc +++ b/gcc/tree-vect-stmts.cc @@ -20,6 +20,7 @@ along with GCC; see the file COPYING3. If not see <http://www.gnu.org/licenses/>. */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" diff --git a/gcc/tree-vectorizer.cc b/gcc/tree-vectorizer.cc index 1fb4fb3..0efabcb 100644 --- a/gcc/tree-vectorizer.cc +++ b/gcc/tree-vectorizer.cc @@ -55,6 +55,7 @@ along with GCC; see the file COPYING3. If not see */ #include "config.h" +#define INCLUDE_MEMORY #include "system.h" #include "coretypes.h" #include "backend.h" |