https://gcc.gnu.org/g:5f88037941d6fbfef7281f767dcc31a5aa61f0cd
commit r16-2548-g5f88037941d6fbfef7281f767dcc31a5aa61f0cd Author: David Malcolm <dmalc...@redhat.com> Date: Fri Jul 25 15:13:44 2025 -0400 diagnostics: move file_cache from input.{cc,h} to diagnostics/file-cache.{cc,h} No functional change intended. gcc/ChangeLog: * Makefile.in (OBJS-libcommon): Add diagnostics/file-cache.o. * diagnostics/changes.cc: Update for file_cache and char_span moving from input.h to diagnostics/file-cache.h and into the "diagnostics::" namespace. * diagnostics/context.cc: Likewise. * diagnostics/diagnostics-selftests.cc: Likewise. * diagnostics/diagnostics-selftests.h: Likewise. * diagnostics/file-cache.cc: New file, based on the file_cache and file_cache_slot material in input.cc. * diagnostics/file-cache.h: Likewise for input.h. * diagnostics/selftest-source-printing.h: Update for file_cache and char_span moving from input.h to diagnostics/file-cache.h and into the "diagnostics::" namespace. * diagnostics/source-printing.cc: Likewise. * final.cc: Likewise. * gcc-rich-location.cc: Likewise. * input.cc (default_charset_callback): Move to diagnostics/file-cache.cc. (file_cache::initialize_input_context): Likewise. (class file_cache_slot): Likewise. (file_cache::tune): Likewise. (file_cache::lookup_file): Likewise. (file_cache::forcibly_evict_file): Likewise. (file_cache::missing_trailing_newline_p): Likewise. (file_cache::add_buffered_content): Likewise. (file_cache::evicted_cache_tab_entry): Likewise. (file_cache::add_file): Likewise. (file_cache::file_cache): Likewise. (file_cache::dump): Likewise. (file_cache::dump): Likewise. (file_cache::lookup_or_add_file): Likewise. (find_end_of_line): Likewise. (file_cache::get_source_line): Likewise. (check_line): Likewise. (test_replacement): Likewise. (test_reading_source_line): Likewise. (test_reading_source_buffer): Likewise. * input.h (class char_span): Move to diagnostics/file-cache.h and into the "diagnostics::" namespace. (class file_cache_slot): Likewise. (class file_cache): Likewise. * libgdiagnostics.cc: Update for file_cache and char_span moving from input.h to diagnostics/file-cache.h and into the "diagnostics::" namespace. * selftest.cc: Likewise. * selftest.h: Likewise. * substring-locations.h: Likewise. * toplev.cc: Likewise. gcc/c-family/ChangeLog: * c-format.cc: Update for file_cache and char_span moving from input.h to diagnostics/file-cache.h and into the "diagnostics::" namespace. * c-indentation.cc: Likewise. gcc/testsuite/ChangeLog: * gcc.dg/plugin/diagnostic_plugin_test_show_locus.cc: Update for file_cache and char_span moving from input.h to diagnostics/file-cache.h and into the "diagnostics::" namespace. Signed-off-by: David Malcolm <dmalc...@redhat.com> Diff: --- gcc/Makefile.in | 1 + gcc/c-family/c-format.cc | 6 +- gcc/c-family/c-indentation.cc | 19 +- gcc/diagnostics/changes.cc | 1 + gcc/diagnostics/context.cc | 1 + gcc/diagnostics/diagnostics-selftests.cc | 1 + gcc/diagnostics/diagnostics-selftests.h | 1 + gcc/diagnostics/file-cache.cc | 1083 ++++++++++++++++++++ gcc/diagnostics/file-cache.h | 125 +++ gcc/diagnostics/selftest-source-printing.h | 1 + gcc/diagnostics/source-printing.cc | 64 +- gcc/final.cc | 3 +- gcc/gcc-rich-location.cc | 7 +- gcc/input.cc | 1069 +------------------ gcc/input.h | 104 +- gcc/libgdiagnostics.cc | 3 +- gcc/selftest.cc | 5 +- gcc/selftest.h | 8 +- gcc/substring-locations.h | 2 +- .../plugin/diagnostic_plugin_test_show_locus.cc | 8 +- gcc/toplev.cc | 1 + 21 files changed, 1308 insertions(+), 1205 deletions(-) diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 24d7b885319c..7314a3b42252 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1858,6 +1858,7 @@ OBJS-libcommon = \ diagnostics/color.o \ diagnostics/context.o \ diagnostics/digraphs.o \ + diagnostics/file-cache.o \ diagnostics/output-spec.o \ diagnostics/html-sink.o \ diagnostics/sarif-sink.o \ diff --git a/gcc/c-family/c-format.cc b/gcc/c-family/c-format.cc index 6934087ce5c3..c8e00b27181c 100644 --- a/gcc/c-family/c-format.cc +++ b/gcc/c-family/c-format.cc @@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. If not see #include "substring-locations.h" #include "selftest.h" #include "diagnostics/selftest-context.h" +#include "diagnostics/file-cache.h" #include "builtins.h" #include "attribs.h" #include "c-family/c-type-mismatch.h" @@ -4634,7 +4635,7 @@ get_corrected_substring (const substring_loc &fmt_loc, if (caret.column > finish.column) return NULL; - char_span line + diagnostics::char_span line = global_dc->get_file_cache ().get_source_line (start.file, start.line); if (!line) return NULL; @@ -4646,7 +4647,8 @@ get_corrected_substring (const substring_loc &fmt_loc, specification, up to the (but not including) the length modifier. In the above example, this would be "%-+*.*". */ int length_up_to_type = caret.column - start.column; - char_span prefix_span = line.subspan (start.column - 1, length_up_to_type); + diagnostics::char_span prefix_span + = line.subspan (start.column - 1, length_up_to_type); char *prefix = prefix_span.xstrdup (); /* Now attempt to generate a suggestion for the rest of the specification diff --git a/gcc/c-family/c-indentation.cc b/gcc/c-family/c-indentation.cc index 2e8261d4977b..bb214fc259b7 100644 --- a/gcc/c-family/c-indentation.cc +++ b/gcc/c-family/c-indentation.cc @@ -25,6 +25,7 @@ along with GCC; see the file COPYING3. If not see #include "c-indentation.h" #include "selftest.h" #include "diagnostic.h" +#include "diagnostics/file-cache.h" /* Round up VIS_COLUMN to nearest tab stop. */ @@ -45,13 +46,13 @@ next_tab_stop (unsigned int vis_column, unsigned int tab_width) on the line (up to or before EXPLOC). */ static bool -get_visual_column (file_cache &fc, +get_visual_column (diagnostics::file_cache &fc, expanded_location exploc, unsigned int *out, unsigned int *first_nws, unsigned int tab_width) { - char_span line = fc.get_source_line (exploc.file, exploc.line); + diagnostics::char_span line = fc.get_source_line (exploc.file, exploc.line); if (!line) return false; if ((size_t)exploc.column > line.length ()) @@ -88,14 +89,14 @@ get_visual_column (file_cache &fc, Otherwise, return false, leaving *FIRST_NWS untouched. */ static bool -get_first_nws_vis_column (file_cache &fc, +get_first_nws_vis_column (diagnostics::file_cache &fc, const char *file, int line_num, unsigned int *first_nws, unsigned int tab_width) { gcc_assert (first_nws); - char_span line = fc.get_source_line (file, line_num); + diagnostics::char_span line = fc.get_source_line (file, line_num); if (!line) return false; unsigned int vis_column = 0; @@ -160,7 +161,7 @@ get_first_nws_vis_column (file_cache &fc, Return true if such an unindent/outdent is detected. */ static bool -detect_intervening_unindent (file_cache &fc, +detect_intervening_unindent (diagnostics::file_cache &fc, const char *file, int body_line, int next_stmt_line, @@ -335,7 +336,7 @@ should_warn_for_misleading_indentation (const token_indent_info &guard_tinfo, if (next_stmt_exploc.file != body_exploc.file) return false; - file_cache &fc = global_dc->get_file_cache (); + diagnostics::file_cache &fc = global_dc->get_file_cache (); /* If NEXT_STMT_LOC and BODY_LOC are on the same line, consider the location of the guard. @@ -691,7 +692,7 @@ test_next_tab_stop () static void assert_get_visual_column_succeeds (const location &loc, - file_cache &fc, + diagnostics::file_cache &fc, const char *file, int line, int column, const unsigned int tab_width, unsigned int expected_visual_column, @@ -735,7 +736,7 @@ assert_get_visual_column_succeeds (const location &loc, static void assert_get_visual_column_fails (const location &loc, - file_cache &fc, + diagnostics::file_cache &fc, const char *file, int line, int column, const unsigned int tab_width) { @@ -783,7 +784,7 @@ test_get_visual_column () "\t line 2\n"); line_table_test ltt; temp_source_file tmp (SELFTEST_LOCATION, ".txt", content); - file_cache fc; + diagnostics::file_cache fc; const unsigned int tab_width = 8; const char *file = tmp.get_filename (); diff --git a/gcc/diagnostics/changes.cc b/gcc/diagnostics/changes.cc index 4dce4c7b64fe..290d6022b5ae 100644 --- a/gcc/diagnostics/changes.cc +++ b/gcc/diagnostics/changes.cc @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "diagnostics/changes.h" #include "pretty-print.h" #include "diagnostics/color.h" +#include "diagnostics/file-cache.h" #include "selftest.h" namespace diagnostics { diff --git a/gcc/diagnostics/context.cc b/gcc/diagnostics/context.cc index f91f13f5d7df..64bb315bd1fa 100644 --- a/gcc/diagnostics/context.cc +++ b/gcc/diagnostics/context.cc @@ -49,6 +49,7 @@ along with GCC; see the file COPYING3. If not see #include "pretty-print-urlifier.h" #include "diagnostics/logical-locations.h" #include "diagnostics/buffering.h" +#include "diagnostics/file-cache.h" #ifdef HAVE_TERMIOS_H # include <termios.h> diff --git a/gcc/diagnostics/diagnostics-selftests.cc b/gcc/diagnostics/diagnostics-selftests.cc index b6fcf08aac1c..94a212a6c93a 100644 --- a/gcc/diagnostics/diagnostics-selftests.cc +++ b/gcc/diagnostics/diagnostics-selftests.cc @@ -40,6 +40,7 @@ void run_diagnostics_selftests () { color_cc_tests (); + file_cache_cc_tests (); source_printing_cc_tests (); html_sink_cc_tests (); sarif_sink_cc_tests (); diff --git a/gcc/diagnostics/diagnostics-selftests.h b/gcc/diagnostics/diagnostics-selftests.h index 501814d8ffb2..994ebad52804 100644 --- a/gcc/diagnostics/diagnostics-selftests.h +++ b/gcc/diagnostics/diagnostics-selftests.h @@ -36,6 +36,7 @@ extern void changes_cc_tests (); extern void color_cc_tests (); extern void context_cc_tests (); extern void digraphs_cc_tests (); +extern void file_cache_cc_tests (); extern void html_sink_cc_tests (); extern void lazy_paths_cc_tests (); extern void output_spec_cc_tests (); diff --git a/gcc/diagnostics/file-cache.cc b/gcc/diagnostics/file-cache.cc new file mode 100644 index 000000000000..febeb031f1bf --- /dev/null +++ b/gcc/diagnostics/file-cache.cc @@ -0,0 +1,1083 @@ +/* Caching input files for use by diagnostics. + Copyright (C) 2004-2025 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "cpplib.h" +#include "diagnostics/file-cache.h" +#include "selftest.h" + +#ifndef HAVE_ICONV +#define HAVE_ICONV 0 +#endif + +namespace diagnostics { + +/* Input charset configuration. */ +static const char *default_charset_callback (const char *) +{ + return nullptr; +} + +void +file_cache::initialize_input_context (diagnostic_input_charset_callback ccb, + bool should_skip_bom) +{ + m_input_context.ccb = (ccb ? ccb : default_charset_callback); + m_input_context.should_skip_bom = should_skip_bom; +} + +/* This is a cache used by get_next_line to store the content of a + file to be searched for file lines. */ +class file_cache_slot +{ +public: + file_cache_slot (); + ~file_cache_slot (); + + void dump (FILE *out, int indent) const; + void DEBUG_FUNCTION dump () const { dump (stderr, 0); } + + bool read_line_num (size_t line_num, + char ** line, ssize_t *line_len); + + /* Accessors. */ + const char *get_file_path () const { return m_file_path; } + unsigned get_use_count () const { return m_use_count; } + bool missing_trailing_newline_p () const + { + return m_missing_trailing_newline; + } + char_span get_full_file_content (); + + void inc_use_count () { m_use_count++; } + + bool create (const file_cache::input_context &in_context, + const char *file_path, FILE *fp, unsigned highest_use_count); + void evict (); + void set_content (const char *buf, size_t sz); + + static size_t tune (size_t line_record_size_) + { + size_t ret = line_record_size; + line_record_size = line_record_size_; + return ret; + } + + private: + /* These are information used to store a line boundary. */ + class line_info + { + public: + /* The line number. It starts from 1. */ + size_t line_num; + + /* The position (byte count) of the beginning of the line, + relative to the file data pointer. This starts at zero. */ + size_t start_pos; + + /* The position (byte count) of the last byte of the line. This + normally points to the '\n' character, or to one byte after the + last byte of the file, if the file doesn't contain a '\n' + character. */ + size_t end_pos; + + line_info (size_t l, size_t s, size_t e) + : line_num (l), start_pos (s), end_pos (e) + {} + + line_info () + :line_num (0), start_pos (0), end_pos (0) + {} + + static bool less_than(const line_info &a, const line_info &b) + { + return a.line_num < b.line_num; + } + }; + + bool needs_read_p () const; + bool needs_grow_p () const; + void maybe_grow (); + bool read_data (); + bool maybe_read_data (); + bool get_next_line (char **line, ssize_t *line_len); + bool read_next_line (char ** line, ssize_t *line_len); + bool goto_next_line (); + + static const size_t buffer_size = 4 * 1024; + static size_t line_record_size; + static size_t recent_cached_lines_shift; + + /* The number of time this file has been accessed. This is used + to designate which file cache to evict from the cache + array. */ + unsigned m_use_count; + + /* The file_path is the key for identifying a particular file in + the cache. This copy is owned by the slot. */ + char *m_file_path; + + FILE *m_fp; + + /* True when an read error happened. */ + bool m_error; + + /* This points to the content of the file that we've read so + far. */ + char *m_data; + + /* The allocated buffer to be freed may start a little earlier than DATA, + e.g. if a UTF8 BOM was skipped at the beginning. */ + int m_alloc_offset; + + /* The size of the DATA array above.*/ + size_t m_size; + + /* The number of bytes read from the underlying file so far. This + must be less (or equal) than SIZE above. */ + size_t m_nb_read; + + /* The index of the beginning of the current line. */ + size_t m_line_start_idx; + + /* The number of the previous line read. This starts at 1. Zero + means we've read no line so far. */ + size_t m_line_num; + + /* Could this file be missing a trailing newline on its final line? + Initially true (to cope with empty files), set to true/false + as each line is read. */ + bool m_missing_trailing_newline; + + /* This is a record of the beginning and end of the lines we've seen + while reading the file. This is useful to avoid walking the data + from the beginning when we are asked to read a line that is + before LINE_START_IDX above. When the lines exceed line_record_size + this is scaled down dynamically, with the line_info becoming anchors. */ + vec<line_info, va_heap> m_line_record; + + /* A cache of the recently seen lines. This is maintained as a ring + buffer. */ + vec<line_info, va_heap> m_line_recent; + + /* First and last valid entry in m_line_recent. */ + size_t m_line_recent_last, m_line_recent_first; + + void offset_buffer (int offset) + { + gcc_assert (offset < 0 ? m_alloc_offset + offset >= 0 + : (size_t) offset <= m_size); + gcc_assert (m_data); + m_alloc_offset += offset; + m_data += offset; + m_size -= offset; + } + +}; + +size_t file_cache_slot::line_record_size = 0; +size_t file_cache_slot::recent_cached_lines_shift = 8; + +/* Tune file_cache. */ +void +file_cache::tune (size_t num_file_slots, size_t lines) +{ + if (file_cache_slot::tune (lines) != lines + || m_num_file_slots != num_file_slots) + { + delete[] m_file_slots; + m_file_slots = new file_cache_slot[num_file_slots]; + } + m_num_file_slots = num_file_slots; +} + +static const char * +find_end_of_line (const char *s, size_t len); + +/* Lookup the cache used for the content of a given file accessed by + caret diagnostic. Return the found cached file, or NULL if no + cached file was found. */ + +file_cache_slot * +file_cache::lookup_file (const char *file_path) +{ + gcc_assert (file_path); + + /* This will contain the found cached file. */ + file_cache_slot *r = NULL; + for (unsigned i = 0; i < m_num_file_slots; ++i) + { + file_cache_slot *c = &m_file_slots[i]; + if (c->get_file_path () && !strcmp (c->get_file_path (), file_path)) + { + c->inc_use_count (); + r = c; + } + } + + if (r) + r->inc_use_count (); + + return r; +} + +/* Purge any mention of FILENAME from the cache of files used for + printing source code. For use in selftests when working + with tempfiles. */ + +void +file_cache::forcibly_evict_file (const char *file_path) +{ + gcc_assert (file_path); + + file_cache_slot *r = lookup_file (file_path); + if (!r) + /* Not found. */ + return; + + r->evict (); +} + +/* Determine if FILE_PATH missing a trailing newline on its final line. + Only valid to call once all of the file has been loaded, by + requesting a line number beyond the end of the file. */ + +bool +file_cache::missing_trailing_newline_p (const char *file_path) +{ + gcc_assert (file_path); + + file_cache_slot *r = lookup_or_add_file (file_path); + return r->missing_trailing_newline_p (); +} + +void +file_cache::add_buffered_content (const char *file_path, + const char *buffer, + size_t sz) +{ + gcc_assert (file_path); + + file_cache_slot *r = lookup_file (file_path); + if (!r) + { + unsigned highest_use_count = 0; + r = evicted_cache_tab_entry (&highest_use_count); + if (!r->create (m_input_context, file_path, nullptr, highest_use_count)) + return; + } + + r->set_content (buffer, sz); +} + +void +file_cache_slot::evict () +{ + free (m_file_path); + m_file_path = NULL; + if (m_fp) + fclose (m_fp); + m_error = false; + m_fp = NULL; + m_nb_read = 0; + m_line_start_idx = 0; + m_line_num = 0; + m_line_record.truncate (0); + m_line_recent_first = 0; + m_line_recent_last = 0; + m_use_count = 0; + m_missing_trailing_newline = true; +} + +/* Return the file cache that has been less used, recently, or the + first empty one. If HIGHEST_USE_COUNT is non-null, + *HIGHEST_USE_COUNT is set to the highest use count of the entries + in the cache table. */ + +file_cache_slot* +file_cache::evicted_cache_tab_entry (unsigned *highest_use_count) +{ + file_cache_slot *to_evict = &m_file_slots[0]; + unsigned huc = to_evict->get_use_count (); + for (unsigned i = 1; i < m_num_file_slots; ++i) + { + file_cache_slot *c = &m_file_slots[i]; + bool c_is_empty = (c->get_file_path () == NULL); + + if (c->get_use_count () < to_evict->get_use_count () + || (to_evict->get_file_path () && c_is_empty)) + /* We evict C because it's either an entry with a lower use + count or one that is empty. */ + to_evict = c; + + if (huc < c->get_use_count ()) + huc = c->get_use_count (); + + if (c_is_empty) + /* We've reached the end of the cache; subsequent elements are + all empty. */ + break; + } + + if (highest_use_count) + *highest_use_count = huc; + + return to_evict; +} + +/* Create the cache used for the content of a given file to be + accessed by caret diagnostic. This cache is added to an array of + cache and can be retrieved by lookup_file_in_cache_tab. This + function returns the created cache. Note that only the last + m_num_file_slots files are cached. + + This can return nullptr if the FILE_PATH can't be opened for + reading, or if the content can't be converted to the input_charset. */ + +file_cache_slot* +file_cache::add_file (const char *file_path) +{ + + FILE *fp = fopen (file_path, "r"); + if (fp == NULL) + return NULL; + + unsigned highest_use_count = 0; + file_cache_slot *r = evicted_cache_tab_entry (&highest_use_count); + if (!r->create (m_input_context, file_path, fp, highest_use_count)) + return NULL; + return r; +} + +/* Get a borrowed char_span to the full content of this file + as decoded according to the input charset, encoded as UTF-8. */ + +char_span +file_cache_slot::get_full_file_content () +{ + char *line; + ssize_t line_len; + while (get_next_line (&line, &line_len)) + { + } + return char_span (m_data, m_nb_read); +} + +/* Populate this slot for use on FILE_PATH and FP, dropping any + existing cached content within it. */ + +bool +file_cache_slot::create (const file_cache::input_context &in_context, + const char *file_path, FILE *fp, + unsigned highest_use_count) +{ + m_file_path = file_path ? xstrdup (file_path) : nullptr; + if (m_fp) + fclose (m_fp); + m_error = false; + m_fp = fp; + if (m_alloc_offset) + offset_buffer (-m_alloc_offset); + m_nb_read = 0; + m_line_start_idx = 0; + m_line_num = 0; + m_line_recent_first = 0; + m_line_recent_last = 0; + m_line_record.truncate (0); + /* Ensure that this cache entry doesn't get evicted next time + add_file_to_cache_tab is called. */ + m_use_count = ++highest_use_count; + m_missing_trailing_newline = true; + + + /* Check the input configuration to determine if we need to do any + transformations, such as charset conversion or BOM skipping. */ + if (const char *input_charset = in_context.ccb (file_path)) + { + /* Need a full-blown conversion of the input charset. */ + fclose (m_fp); + m_fp = NULL; + const cpp_converted_source cs + = cpp_get_converted_source (file_path, input_charset); + if (!cs.data) + return false; + if (m_data) + XDELETEVEC (m_data); + m_data = cs.data; + m_nb_read = m_size = cs.len; + m_alloc_offset = cs.data - cs.to_free; + } + else if (in_context.should_skip_bom) + { + if (read_data ()) + { + const int offset = cpp_check_utf8_bom (m_data, m_nb_read); + offset_buffer (offset); + m_nb_read -= offset; + } + } + + return true; +} + +void +file_cache_slot::set_content (const char *buf, size_t sz) +{ + m_data = (char *)xmalloc (sz); + memcpy (m_data, buf, sz); + m_nb_read = m_size = sz; + m_alloc_offset = 0; + + if (m_fp) + { + fclose (m_fp); + m_fp = nullptr; + } +} + +/* file_cache's ctor. */ + +file_cache::file_cache () +: m_num_file_slots (16), m_file_slots (new file_cache_slot[m_num_file_slots]) +{ + initialize_input_context (nullptr, false); +} + +/* file_cache's dtor. */ + +file_cache::~file_cache () +{ + delete[] m_file_slots; +} + +void +file_cache::dump (FILE *out, int indent) const +{ + for (size_t i = 0; i < m_num_file_slots; ++i) + { + fprintf (out, "%*sslot[%i]:\n", indent, "", (int)i); + m_file_slots[i].dump (out, indent + 2); + } +} + +void +file_cache::dump () const +{ + dump (stderr, 0); +} + +/* Lookup the cache used for the content of a given file accessed by + caret diagnostic. If no cached file was found, create a new cache + for this file, add it to the array of cached file and return + it. + + This can return nullptr on a cache miss if FILE_PATH can't be opened for + reading, or if the content can't be converted to the input_charset. */ + +file_cache_slot* +file_cache::lookup_or_add_file (const char *file_path) +{ + file_cache_slot *r = lookup_file (file_path); + if (r == NULL) + r = add_file (file_path); + return r; +} + +/* Default constructor for a cache of file used by caret + diagnostic. */ + +file_cache_slot::file_cache_slot () +: m_use_count (0), m_file_path (NULL), m_fp (NULL), m_error (false), m_data (0), + m_alloc_offset (0), m_size (0), m_nb_read (0), m_line_start_idx (0), + m_line_num (0), m_missing_trailing_newline (true), + m_line_recent_last (0), m_line_recent_first (0) +{ + m_line_record.create (0); + m_line_recent.create (1U << recent_cached_lines_shift); + for (int i = 0; i < 1 << recent_cached_lines_shift; i++) + m_line_recent.quick_push (file_cache_slot::line_info (0, 0, 0)); +} + +/* Destructor for a cache of file used by caret diagnostic. */ + +file_cache_slot::~file_cache_slot () +{ + free (m_file_path); + if (m_fp) + { + fclose (m_fp); + m_fp = NULL; + } + if (m_data) + { + offset_buffer (-m_alloc_offset); + XDELETEVEC (m_data); + m_data = 0; + } + m_line_record.release (); + m_line_recent.release (); +} + +void +file_cache_slot::dump (FILE *out, int indent) const +{ + if (!m_file_path) + { + fprintf (out, "%*s(unused)\n", indent, ""); + return; + } + fprintf (out, "%*sfile_path: %s\n", indent, "", m_file_path); + fprintf (out, "%*sfp: %p\n", indent, "", (void *)m_fp); + fprintf (out, "%*sneeds_read_p: %i\n", indent, "", (int)needs_read_p ()); + fprintf (out, "%*sneeds_grow_p: %i\n", indent, "", (int)needs_grow_p ()); + fprintf (out, "%*suse_count: %i\n", indent, "", m_use_count); + fprintf (out, "%*ssize: %zi\n", indent, "", m_size); + fprintf (out, "%*snb_read: %zi\n", indent, "", m_nb_read); + fprintf (out, "%*sstart_line_idx: %zi\n", indent, "", m_line_start_idx); + fprintf (out, "%*sline_num: %zi\n", indent, "", m_line_num); + fprintf (out, "%*smissing_trailing_newline: %i\n", + indent, "", (int)m_missing_trailing_newline); + fprintf (out, "%*sline records (%i):\n", + indent, "", m_line_record.length ()); + int idx = 0; + for (auto &line : m_line_record) + fprintf (out, "%*s[%i]: line %zi: byte offsets: %zi-%zi\n", + indent + 2, "", + idx++, line.line_num, line.start_pos, line.end_pos); +} + +/* Returns TRUE iff the cache would need to be filled with data coming + from the file. That is, either the cache is empty or full or the + current line is empty. Note that if the cache is full, it would + need to be extended and filled again. */ + +bool +file_cache_slot::needs_read_p () const +{ + return m_fp && (m_nb_read == 0 + || m_nb_read == m_size + || (m_line_start_idx >= m_nb_read - 1)); +} + +/* Return TRUE iff the cache is full and thus needs to be + extended. */ + +bool +file_cache_slot::needs_grow_p () const +{ + return m_nb_read == m_size; +} + +/* Grow the cache if it needs to be extended. */ + +void +file_cache_slot::maybe_grow () +{ + if (!needs_grow_p ()) + return; + + if (!m_data) + { + gcc_assert (m_size == 0 && m_alloc_offset == 0); + m_size = buffer_size; + m_data = XNEWVEC (char, m_size); + } + else + { + const int offset = m_alloc_offset; + offset_buffer (-offset); + m_size *= 2; + m_data = XRESIZEVEC (char, m_data, m_size); + offset_buffer (offset); + } +} + +/* Read more data into the cache. Extends the cache if need be. + Returns TRUE iff new data could be read. */ + +bool +file_cache_slot::read_data () +{ + if (feof (m_fp) || ferror (m_fp)) + return false; + + maybe_grow (); + + char * from = m_data + m_nb_read; + size_t to_read = m_size - m_nb_read; + size_t nb_read = fread (from, 1, to_read, m_fp); + + if (ferror (m_fp)) + { + m_error = true; + return false; + } + + m_nb_read += nb_read; + return !!nb_read; +} + +/* Read new data iff the cache needs to be filled with more data + coming from the file FP. Return TRUE iff the cache was filled with + mode data. */ + +bool +file_cache_slot::maybe_read_data () +{ + if (!needs_read_p ()) + return false; + return read_data (); +} + +/* Helper function for file_cache_slot::get_next_line (), to find the end of + the next line. Returns with the memchr convention, i.e. nullptr if a line + terminator was not found. We need to determine line endings in the same + manner that libcpp does: any of \n, \r\n, or \r is a line ending. */ + +static const char * +find_end_of_line (const char *s, size_t len) +{ + for (const auto end = s + len; s != end; ++s) + { + if (*s == '\n') + return s; + if (*s == '\r') + { + const auto next = s + 1; + if (next == end) + { + /* Don't find the line ending if \r is the very last character + in the buffer; we do not know if it's the end of the file or + just the end of what has been read so far, and we wouldn't + want to break in the middle of what's actually a \r\n + sequence. Instead, we will handle the case of a file ending + in a \r later. */ + break; + } + return (*next == '\n' ? next : s); + } + } + return nullptr; +} + +/* Read a new line from file FP, using C as a cache for the data + coming from the file. Upon successful completion, *LINE is set to + the beginning of the line found. *LINE points directly in the + line cache and is only valid until the next call of get_next_line. + *LINE_LEN is set to the length of the line. Note that the line + does not contain any terminal delimiter. This function returns + true if some data was read or process from the cache, false + otherwise. Note that subsequent calls to get_next_line might + make the content of *LINE invalid. */ + +bool +file_cache_slot::get_next_line (char **line, ssize_t *line_len) +{ + /* Fill the cache with data to process. */ + maybe_read_data (); + + size_t remaining_size = m_nb_read - m_line_start_idx; + if (remaining_size == 0) + /* There is no more data to process. */ + return false; + + const char *line_start = m_data + m_line_start_idx; + + const char *next_line_start = NULL; + size_t len = 0; + const char *line_end = find_end_of_line (line_start, remaining_size); + if (line_end == NULL) + { + /* We haven't found an end-of-line delimiter in the cache. + Fill the cache with more data from the file and look again. */ + while (maybe_read_data ()) + { + line_start = m_data + m_line_start_idx; + remaining_size = m_nb_read - m_line_start_idx; + line_end = find_end_of_line (line_start, remaining_size); + if (line_end != NULL) + { + next_line_start = line_end + 1; + break; + } + } + if (line_end == NULL) + { + /* We've loaded all the file into the cache and still no + terminator. Let's say the line ends up at one byte past the + end of the file. This is to stay consistent with the case + of when the line ends up with a terminator and line_end points to + that. That consistency is useful below in the len calculation. + + If the file ends in a \r, we didn't identify it as a line + terminator above, so do that now instead. */ + line_end = m_data + m_nb_read; + if (m_nb_read && line_end[-1] == '\r') + { + --line_end; + m_missing_trailing_newline = false; + } + else + m_missing_trailing_newline = true; + } + else + m_missing_trailing_newline = false; + } + else + { + next_line_start = line_end + 1; + m_missing_trailing_newline = false; + } + + if (m_error) + return false; + + /* At this point, we've found the end of the of line. It either points to + the line terminator or to one byte after the last byte of the file. */ + gcc_assert (line_end != NULL); + + len = line_end - line_start; + + if (m_line_start_idx < m_nb_read) + *line = const_cast<char *> (line_start); + + ++m_line_num; + + /* Now update our line record so that re-reading lines from the + before m_line_start_idx is faster. */ + size_t rlen = m_line_record.length (); + /* Only update when beyond the previously cached region. */ + if (rlen == 0 || m_line_record[rlen - 1].line_num < m_line_num) + { + size_t spacing + = (rlen >= 2 + ? (m_line_record[rlen - 1].line_num + - m_line_record[rlen - 2].line_num) : 1); + size_t delta + = rlen >= 1 ? m_line_num - m_line_record[rlen - 1].line_num : 1; + + size_t max_size = line_record_size; + /* One anchor per hundred input lines. */ + if (max_size == 0) + max_size = m_line_num / 100; + + /* If we're too far beyond drop half of the lines to rebalance. */ + if (rlen == max_size && delta >= spacing * 2) + { + size_t j = 0; + for (size_t i = 1; i < rlen; i += 2) + m_line_record[j++] = m_line_record[i]; + m_line_record.truncate (j); + rlen = j; + spacing *= 2; + } + + if (rlen < max_size && delta >= spacing) + { + file_cache_slot::line_info li (m_line_num, m_line_start_idx, + line_end - m_data); + m_line_record.safe_push (li); + } + } + + /* Cache recent tail lines separately for fast access. This assumes + most accesses do not skip backwards. */ + if (m_line_recent_last == m_line_recent_first + || m_line_recent[m_line_recent_last].line_num == m_line_num - 1) + { + size_t mask = ((size_t) 1 << recent_cached_lines_shift) - 1; + m_line_recent_last = (m_line_recent_last + 1) & mask; + if (m_line_recent_last == m_line_recent_first) + m_line_recent_first = (m_line_recent_first + 1) & mask; + m_line_recent[m_line_recent_last] + = file_cache_slot::line_info (m_line_num, m_line_start_idx, + line_end - m_data); + } + + /* Update m_line_start_idx so that it points to the next line to be + read. */ + if (next_line_start) + m_line_start_idx = next_line_start - m_data; + else + /* We didn't find any terminal '\n'. Let's consider that the end + of line is the end of the data in the cache. The next + invocation of get_next_line will either read more data from the + underlying file or return false early because we've reached the + end of the file. */ + m_line_start_idx = m_nb_read; + + *line_len = len; + + return true; +} + +/* Consume the next bytes coming from the cache (or from its + underlying file if there are remaining unread bytes in the file) + until we reach the next end-of-line (or end-of-file). There is no + copying from the cache involved. Return TRUE upon successful + completion. */ + +bool +file_cache_slot::goto_next_line () +{ + char *l; + ssize_t len; + + return get_next_line (&l, &len); +} + +/* Read an arbitrary line number LINE_NUM from the file cached in C. + If the line was read successfully, *LINE points to the beginning + of the line in the file cache and *LINE_LEN is the length of the + line. *LINE is not nul-terminated, but may contain zero bytes. + *LINE is only valid until the next call of read_line_num. + This function returns bool if a line was read. */ + +bool +file_cache_slot::read_line_num (size_t line_num, + char ** line, ssize_t *line_len) +{ + gcc_assert (line_num > 0); + + /* Is the line in the recent line cache? + This assumes the main file processing is only using + a single contiguous cursor with only temporary excursions. */ + if (m_line_recent_first != m_line_recent_last + && m_line_recent[m_line_recent_first].line_num <= line_num + && m_line_recent[m_line_recent_last].line_num >= line_num) + { + line_info &last = m_line_recent[m_line_recent_last]; + size_t mask = (1U << recent_cached_lines_shift) - 1; + size_t idx = (m_line_recent_last - (last.line_num - line_num)) & mask; + line_info &recent = m_line_recent[idx]; + gcc_assert (recent.line_num == line_num); + *line = m_data + recent.start_pos; + *line_len = recent.end_pos - recent.start_pos; + return true; + } + + if (line_num <= m_line_num) + { + line_info l (line_num, 0, 0); + int i = m_line_record.lower_bound (l, line_info::less_than); + if (i == 0) + { + m_line_start_idx = 0; + m_line_num = 0; + } + else if (m_line_record[i - 1].line_num == line_num) + { + /* We have the start/end of the line. */ + *line = m_data + m_line_record[i - 1].start_pos; + *line_len = m_line_record[i - 1].end_pos - m_line_record[i - 1].start_pos; + return true; + } + else + { + gcc_assert (m_line_record[i - 1].line_num < m_line_num); + m_line_start_idx = m_line_record[i - 1].start_pos; + m_line_num = m_line_record[i - 1].line_num - 1; + } + } + + /* Let's walk from line m_line_num up to line_num - 1, without + copying any line. */ + while (m_line_num < line_num - 1) + if (!goto_next_line ()) + return false; + + /* The line we want is the next one. Let's read it. */ + return get_next_line (line, line_len); +} + +/* Return the physical source line that corresponds to FILE_PATH/LINE. + The line is not nul-terminated. The returned pointer is only + valid until the next call of location_get_source_line. + Note that the line can contain several null characters, + so the returned value's length has the actual length of the line. + If the function fails, a NULL char_span is returned. */ + +char_span +file_cache::get_source_line (const char *file_path, int line) +{ + char *buffer = NULL; + ssize_t len; + + if (line == 0) + return char_span (NULL, 0); + + if (file_path == NULL) + return char_span (NULL, 0); + + file_cache_slot *c = lookup_or_add_file (file_path); + if (c == NULL) + return char_span (NULL, 0); + + bool read = c->read_line_num (line, &buffer, &len); + if (!read) + return char_span (NULL, 0); + + return char_span (buffer, len); +} + +char_span +file_cache::get_source_file_content (const char *file_path) +{ + file_cache_slot *c = lookup_or_add_file (file_path); + if (c == nullptr) + return char_span (nullptr, 0); + return c->get_full_file_content (); +} + +#if CHECKING_P + +namespace selftest { + + using temp_source_file = ::selftest::temp_source_file; + +/* Verify reading of a specific line LINENUM in TMP, FC. */ + +static void +check_line (temp_source_file &tmp, file_cache &fc, int linenum) +{ + char_span line = fc.get_source_line (tmp.get_filename (), linenum); + int n; + const char *b = line.get_buffer (); + size_t l = line.length (); + char buf[5]; + ASSERT_LT (l, 5); + memcpy (buf, b, l); + buf[l] = '\0'; + ASSERT_TRUE (sscanf (buf, "%d", &n) == 1); + ASSERT_EQ (n, linenum); +} + +/* Test file cache replacement. */ + +static void +test_replacement () +{ + const int maxline = 1000; + + char *vec = XNEWVEC (char, maxline * 5); + char *p = vec; + int i; + for (i = 1; i <= maxline; i++) + p += sprintf (p, "%d\n", i); + + temp_source_file tmp (SELFTEST_LOCATION, ".txt", vec); + free (vec); + file_cache fc; + + for (i = 2; i <= maxline; i++) + { + check_line (tmp, fc, i); + check_line (tmp, fc, i - 1); + if (i >= 10) + check_line (tmp, fc, i - 9); + if (i >= 350) /* Exceed the look behind cache. */ + check_line (tmp, fc, i - 300); + } + for (i = 5; i <= maxline; i += 100) + check_line (tmp, fc, i); + for (i = 1; i <= maxline; i++) + check_line (tmp, fc, i); +} + +/* Verify reading of input files (e.g. for caret-based diagnostics). */ + +static void +test_reading_source_line () +{ + /* Create a tempfile and write some text to it. */ + temp_source_file tmp (SELFTEST_LOCATION, ".txt", + "01234567890123456789\n" + "This is the test text\n" + "This is the 3rd line"); + file_cache fc; + + /* Read back a specific line from the tempfile. */ + char_span source_line = fc.get_source_line (tmp.get_filename (), 3); + ASSERT_TRUE (source_line); + ASSERT_TRUE (source_line.get_buffer () != NULL); + ASSERT_EQ (20, source_line.length ()); + ASSERT_TRUE (!strncmp ("This is the 3rd line", + source_line.get_buffer (), source_line.length ())); + + source_line = fc.get_source_line (tmp.get_filename (), 2); + ASSERT_TRUE (source_line); + ASSERT_TRUE (source_line.get_buffer () != NULL); + ASSERT_EQ (21, source_line.length ()); + ASSERT_TRUE (!strncmp ("This is the test text", + source_line.get_buffer (), source_line.length ())); + + source_line = fc.get_source_line (tmp.get_filename (), 4); + ASSERT_FALSE (source_line); + ASSERT_TRUE (source_line.get_buffer () == NULL); +} + +/* Verify reading from buffers (e.g. for sarif-replay). */ + +static void +test_reading_source_buffer () +{ + const char *text = ("01234567890123456789\n" + "This is the test text\n" + "This is the 3rd line"); + const char *filename = "foo.txt"; + file_cache fc; + fc.add_buffered_content (filename, text, strlen (text)); + + /* Read back a specific line from the tempfile. */ + char_span source_line = fc.get_source_line (filename, 3); + ASSERT_TRUE (source_line); + ASSERT_TRUE (source_line.get_buffer () != NULL); + ASSERT_EQ (20, source_line.length ()); + ASSERT_TRUE (!strncmp ("This is the 3rd line", + source_line.get_buffer (), source_line.length ())); + + source_line = fc.get_source_line (filename, 2); + ASSERT_TRUE (source_line); + ASSERT_TRUE (source_line.get_buffer () != NULL); + ASSERT_EQ (21, source_line.length ()); + ASSERT_TRUE (!strncmp ("This is the test text", + source_line.get_buffer (), source_line.length ())); + + source_line = fc.get_source_line (filename, 4); + ASSERT_FALSE (source_line); + ASSERT_TRUE (source_line.get_buffer () == NULL); +} + +/* Run all of the selftests within this file. */ + +void +file_cache_cc_tests () +{ + test_reading_source_line (); + test_reading_source_buffer (); + test_replacement (); +} + +} // namespace selftest + +#endif /* CHECKING_P */ + +} // namespace diagnostics diff --git a/gcc/diagnostics/file-cache.h b/gcc/diagnostics/file-cache.h new file mode 100644 index 000000000000..832a960d5913 --- /dev/null +++ b/gcc/diagnostics/file-cache.h @@ -0,0 +1,125 @@ +/* Caching input files for use by diagnostics. + Copyright (C) 2004-2025 Free Software Foundation, Inc. + +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_DIAGNOSTICS_FILE_CACHE_H +#define GCC_DIAGNOSTICS_FILE_CACHE_H + +namespace diagnostics { + +/* A class capturing the bounds of a buffer, to allow for run-time + bounds-checking in a checked build. */ + +class char_span +{ + public: + char_span (const char *ptr, size_t n_elts) : m_ptr (ptr), m_n_elts (n_elts) {} + + /* Test for a non-NULL pointer. */ + operator bool() const { return m_ptr; } + + /* Get length, not including any 0-terminator (which may not be, + in fact, present). */ + size_t length () const { return m_n_elts; } + + const char *get_buffer () const { return m_ptr; } + + char operator[] (int idx) const + { + gcc_assert (idx >= 0); + gcc_assert ((size_t)idx < m_n_elts); + return m_ptr[idx]; + } + + char_span subspan (int offset, int n_elts) const + { + gcc_assert (offset >= 0); + gcc_assert (offset < (int)m_n_elts); + gcc_assert (n_elts >= 0); + gcc_assert (offset + n_elts <= (int)m_n_elts); + return char_span (m_ptr + offset, n_elts); + } + + char *xstrdup () const + { + return ::xstrndup (m_ptr, m_n_elts); + } + + private: + const char *m_ptr; + size_t m_n_elts; +}; + +/* Forward decl of slot within file_cache, so that the definition doesn't + need to be in this header. */ +class file_cache_slot; + +/* A cache of source files for use when emitting diagnostics + (and in a few places in the C/C++ frontends). + + Results are only valid until the next call to the cache, as + slots can be evicted. + + Filenames are stored by pointer, and so must outlive the cache + instance. */ + +class file_cache +{ + public: + file_cache (); + ~file_cache (); + + void dump (FILE *out, int indent) const; + void DEBUG_FUNCTION dump () const; + + file_cache_slot *lookup_or_add_file (const char *file_path); + void forcibly_evict_file (const char *file_path); + + /* See comments in diagnostic.h about the input conversion context. */ + struct input_context + { + diagnostic_input_charset_callback ccb; + bool should_skip_bom; + }; + void initialize_input_context (diagnostic_input_charset_callback ccb, + bool should_skip_bom); + + char_span get_source_file_content (const char *file_path); + char_span get_source_line (const char *file_path, int line); + bool missing_trailing_newline_p (const char *file_path); + + void add_buffered_content (const char *file_path, + const char *buffer, + size_t sz); + + void tune (size_t num_file_slots, size_t lines); + + private: + file_cache_slot *evicted_cache_tab_entry (unsigned *highest_use_count); + file_cache_slot *add_file (const char *file_path); + file_cache_slot *lookup_file (const char *file_path); + + private: + size_t m_num_file_slots; + file_cache_slot *m_file_slots; + input_context m_input_context; +}; + +} // namespace diagnostics + +#endif // #ifndef GCC_DIAGNOSTICS_FILE_CACHE_H diff --git a/gcc/diagnostics/selftest-source-printing.h b/gcc/diagnostics/selftest-source-printing.h index 8c5866d37cd5..451c12042750 100644 --- a/gcc/diagnostics/selftest-source-printing.h +++ b/gcc/diagnostics/selftest-source-printing.h @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #define GCC_DIAGNOSTICS_SELFTEST_SOURCE_PRINTING_H #include "selftest.h" +#include "diagnostics/file-cache.h" /* The selftest code should entirely disappear in a production configuration, hence we guard all of it with #if CHECKING_P. */ diff --git a/gcc/diagnostics/source-printing.cc b/gcc/diagnostics/source-printing.cc index ec26fa6a08ba..9f06728366e1 100644 --- a/gcc/diagnostics/source-printing.cc +++ b/gcc/diagnostics/source-printing.cc @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see #include "text-art/types.h" #include "text-art/theme.h" #include "diagnostics/source-printing-effects.h" +#include "diagnostics/file-cache.h" #include "xml.h" #include "xml-printer.h" @@ -201,7 +202,7 @@ enum column_unit { class exploc_with_display_col : public expanded_location { public: - exploc_with_display_col (file_cache &fc, + exploc_with_display_col (diagnostics::file_cache &fc, const expanded_location &exploc, const cpp_char_column_policy &policy, enum location_aspect aspect) @@ -814,7 +815,7 @@ class layout const diagnostics::source_printing_options &m_options; const line_maps *m_line_table; - file_cache &m_file_cache; + diagnostics::file_cache &m_file_cache; const text_art::ascii_theme m_fallback_theme; const text_art::theme &m_theme; diagnostics::source_effect_info *m_effect_info; @@ -1250,7 +1251,7 @@ static cpp_char_column_policy def_policy () e.g. in test_diagnostic_show_locus_one_liner_utf8(). */ static layout_range -make_range (file_cache &fc, +make_range (diagnostics::file_cache &fc, int start_line, int start_col, int end_line, int end_col) { const expanded_location start_exploc @@ -1279,7 +1280,7 @@ make_range (file_cache &fc, static void test_layout_range_for_single_point () { - file_cache fc; + diagnostics::file_cache fc; layout_range point = make_range (fc, 7, 10, 7, 10); /* Tests for layout_range::contains_point. */ @@ -1316,7 +1317,7 @@ test_layout_range_for_single_point () static void test_layout_range_for_single_line () { - file_cache fc; + diagnostics::file_cache fc; layout_range example_a = make_range (fc, 2, 22, 2, 38); /* Tests for layout_range::contains_point. */ @@ -1359,7 +1360,7 @@ test_layout_range_for_single_line () static void test_layout_range_for_multiple_lines () { - file_cache fc; + diagnostics::file_cache fc; layout_range example_b = make_range (fc, 3, 14, 5, 8); /* Tests for layout_range::contains_point. */ @@ -2201,8 +2202,9 @@ layout::calculate_x_offset_display () return; } - const char_span line = m_file_cache.get_source_line (m_exploc.file, - m_exploc.line); + const diagnostics::char_span line + = m_file_cache.get_source_line (m_exploc.file, + m_exploc.line); if (!line) { /* Nothing to do, we couldn't find the source line. */ @@ -3159,7 +3161,7 @@ public: /* Get the range of bytes or display columns that HINT would affect. */ static column_range -get_affected_range (file_cache &fc, +get_affected_range (diagnostics::file_cache &fc, const cpp_char_column_policy &policy, const fixit_hint *hint, enum column_unit col_unit) { @@ -3189,7 +3191,7 @@ get_affected_range (file_cache &fc, /* Get the range of display columns that would be printed for HINT. */ static column_range -get_printed_columns (file_cache &fc, +get_printed_columns (diagnostics::file_cache &fc, const cpp_char_column_policy &policy, const fixit_hint *hint) { @@ -3251,7 +3253,7 @@ public: m_display_cols = cpp_display_width (m_text, m_byte_length, m_policy); } - void overwrite (int dst_offset, const char_span &src_span) + void overwrite (int dst_offset, const diagnostics::char_span &src_span) { gcc_assert (dst_offset >= 0); gcc_assert (dst_offset + src_span.length () < m_alloc_sz); @@ -3312,7 +3314,7 @@ correction::ensure_terminated () class line_corrections { public: - line_corrections (file_cache &fc, + line_corrections (diagnostics::file_cache &fc, const char_display_policy &policy, const char *filename, linenum_type row) @@ -3323,7 +3325,7 @@ public: void add_hint (const fixit_hint *hint); - file_cache &m_file_cache; + diagnostics::file_cache &m_file_cache; const char_display_policy &m_policy; const char *m_filename; linenum_type m_row; @@ -3346,9 +3348,12 @@ line_corrections::~line_corrections () class source_line { public: - source_line (file_cache &fc, const char *filename, int line); + source_line (diagnostics::file_cache &fc, const char *filename, int line); - char_span as_span () { return char_span (chars, width); } + diagnostics::char_span as_span () + { + return diagnostics::char_span (chars, width); + } const char *chars; int width; @@ -3356,9 +3361,11 @@ public: /* source_line's ctor. */ -source_line::source_line (file_cache &fc, const char *filename, int line) +source_line::source_line (diagnostics::file_cache &fc, + const char *filename, + int line) { - char_span span = fc.get_source_line (filename, line); + diagnostics::char_span span = fc.get_source_line (filename, line); chars = span.get_buffer (); width = span.length (); } @@ -3422,9 +3429,10 @@ line_corrections::add_hint (const fixit_hint *hint) (old_byte_len, line.as_span ().subspan (between.start - 1, between.finish + 1 - between.start)); - last_correction->overwrite (old_byte_len + between_byte_len, - char_span (hint->get_string (), - hint->get_length ())); + last_correction->overwrite + (old_byte_len + between_byte_len, + diagnostics::char_span (hint->get_string (), + hint->get_length ())); last_correction->m_byte_length = new_byte_len; last_correction->ensure_terminated (); last_correction->m_affected_bytes.finish @@ -3713,7 +3721,7 @@ layout_printer<Sink>::print_line (linenum_type row) { typename Sink::auto_check_tag_nesting sentinel (m_sink); - char_span line + diagnostics::char_span line = m_layout.m_file_cache.get_source_line (m_layout.m_exploc.file, row); if (!line) return; @@ -4215,7 +4223,7 @@ test_layout_x_offset_display_utf8 (const line_table_case &case_) ASSERT_EQ (1, LOCATION_LINE (line_end)); ASSERT_EQ (line_bytes, LOCATION_COLUMN (line_end)); - char_span lspan = f.m_fc.get_source_line (f.get_filename (), 1); + diagnostics::char_span lspan = f.m_fc.get_source_line (f.get_filename (), 1); ASSERT_EQ (line_display_cols, cpp_display_width (lspan.get_buffer (), lspan.length (), def_policy ())); @@ -4367,7 +4375,7 @@ test_layout_x_offset_display_tab (const line_table_case &case_) ASSERT_EQ (7, extra_width[10]); temp_source_file tmp (SELFTEST_LOCATION, ".c", content); - file_cache fc; + diagnostics::file_cache fc; line_table_test ltt (case_); linemap_add (line_table, LC_ENTER, false, tmp.get_filename (), 1); @@ -4379,7 +4387,7 @@ test_layout_x_offset_display_tab (const line_table_case &case_) return; /* Check that cpp_display_width handles the tabs as expected. */ - char_span lspan = fc.get_source_line (tmp.get_filename (), 1); + diagnostics::char_span lspan = fc.get_source_line (tmp.get_filename (), 1); ASSERT_EQ ('\t', *(lspan.get_buffer () + (tab_col - 1))); for (int tabstop = 1; tabstop != num_tabstops; ++tabstop) { @@ -5712,7 +5720,7 @@ test_diagnostic_show_locus_one_liner_utf8 (const line_table_case &case_) ASSERT_EQ (1, LOCATION_LINE (line_end)); ASSERT_EQ (31, LOCATION_COLUMN (line_end)); - char_span lspan = f.m_fc.get_source_line (f.get_filename (), 1); + diagnostics::char_span lspan = f.m_fc.get_source_line (f.get_filename (), 1); ASSERT_EQ (25, cpp_display_width (lspan.get_buffer (), lspan.length (), def_policy ())); ASSERT_EQ (25, location_compute_display_column (f.m_fc, @@ -6057,7 +6065,7 @@ test_overlapped_fixit_printing (const line_table_case &case_) const char *content = (" foo *f = (foo *)ptr->field;\n"); temp_source_file tmp (SELFTEST_LOCATION, ".C", content); - file_cache fc; + diagnostics::file_cache fc; line_table_test ltt (case_); const line_map_ordinary *ord_map @@ -6295,7 +6303,7 @@ test_overlapped_fixit_printing_utf8 (const line_table_case &case_) /* Example where 3 fix-it hints are printed as one. */ { test_context dc; - file_cache &fc = dc.get_file_cache (); + diagnostics::file_cache &fc = dc.get_file_cache (); rich_location richloc (line_table, expr); richloc.add_fixit_replace (open_paren, "const_cast<"); richloc.add_fixit_replace (close_paren, "> ("); @@ -6520,7 +6528,7 @@ test_overlapped_fixit_printing_2 (const line_table_case &case_) /* Two insertions, in the wrong order. */ { test_context dc; - file_cache &fc = dc.get_file_cache (); + diagnostics::file_cache &fc = dc.get_file_cache (); rich_location richloc (line_table, col_20); richloc.add_fixit_insert_before (col_23, "{"); diff --git a/gcc/final.cc b/gcc/final.cc index a4dbab72914b..afcb0bb9efbc 100644 --- a/gcc/final.cc +++ b/gcc/final.cc @@ -83,6 +83,7 @@ along with GCC; see the file COPYING3. If not see #include "function-abi.h" #include "common/common-target.h" #include "diagnostic.h" +#include "diagnostics/file-cache.h" #include "dwarf2out.h" @@ -2116,7 +2117,7 @@ asm_show_source (const char *filename, int linenum) if (!filename) return; - char_span line + diagnostics::char_span line = global_dc->get_file_cache ().get_source_line (filename, linenum); if (!line) return; diff --git a/gcc/gcc-rich-location.cc b/gcc/gcc-rich-location.cc index 20959879321a..beb7b6c9f205 100644 --- a/gcc/gcc-rich-location.cc +++ b/gcc/gcc-rich-location.cc @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3. If not see #include "intl.h" #include "cpplib.h" #include "diagnostic.h" +#include "diagnostics/file-cache.h" /* Add a range to the rich_location, covering expression EXPR, using LABEL if non-NULL. */ @@ -79,11 +80,11 @@ gcc_rich_location::add_fixit_misspelled_id (location_t misspelled_token_loc, /* Return true if there is nothing on LOC's line before LOC. */ static bool -blank_line_before_p (file_cache &fc, +blank_line_before_p (diagnostics::file_cache &fc, location_t loc) { expanded_location exploc = expand_location (loc); - char_span line = fc.get_source_line (exploc.file, exploc.line); + diagnostics::char_span line = fc.get_source_line (exploc.file, exploc.line); if (!line) return false; if (line.length () < (size_t)exploc.column) @@ -101,7 +102,7 @@ blank_line_before_p (file_cache &fc, If true is returned then *OUT_START_OF_LINE is written to. */ static bool -use_new_line (file_cache &fc, +use_new_line (diagnostics::file_cache &fc, location_t insertion_point, location_t indent, location_t *out_start_of_line) { diff --git a/gcc/input.cc b/gcc/input.cc index afd74f6a25f2..b9a553954401 100644 --- a/gcc/input.cc +++ b/gcc/input.cc @@ -22,6 +22,7 @@ along with GCC; see the file COPYING3. If not see #include "coretypes.h" #include "intl.h" #include "diagnostic.h" +#include "diagnostics/file-cache.h" #include "selftest.h" #include "cpplib.h" @@ -35,188 +36,6 @@ special_fname_builtin () return _("<built-in>"); } -/* Input charset configuration. */ -static const char *default_charset_callback (const char *) -{ - return nullptr; -} - -void -file_cache::initialize_input_context (diagnostic_input_charset_callback ccb, - bool should_skip_bom) -{ - m_input_context.ccb = (ccb ? ccb : default_charset_callback); - m_input_context.should_skip_bom = should_skip_bom; -} - -/* This is a cache used by get_next_line to store the content of a - file to be searched for file lines. */ -class file_cache_slot -{ -public: - file_cache_slot (); - ~file_cache_slot (); - - void dump (FILE *out, int indent) const; - void DEBUG_FUNCTION dump () const { dump (stderr, 0); } - - bool read_line_num (size_t line_num, - char ** line, ssize_t *line_len); - - /* Accessors. */ - const char *get_file_path () const { return m_file_path; } - unsigned get_use_count () const { return m_use_count; } - bool missing_trailing_newline_p () const - { - return m_missing_trailing_newline; - } - char_span get_full_file_content (); - - void inc_use_count () { m_use_count++; } - - bool create (const file_cache::input_context &in_context, - const char *file_path, FILE *fp, unsigned highest_use_count); - void evict (); - void set_content (const char *buf, size_t sz); - - static size_t tune (size_t line_record_size_) - { - size_t ret = line_record_size; - line_record_size = line_record_size_; - return ret; - } - - private: - /* These are information used to store a line boundary. */ - class line_info - { - public: - /* The line number. It starts from 1. */ - size_t line_num; - - /* The position (byte count) of the beginning of the line, - relative to the file data pointer. This starts at zero. */ - size_t start_pos; - - /* The position (byte count) of the last byte of the line. This - normally points to the '\n' character, or to one byte after the - last byte of the file, if the file doesn't contain a '\n' - character. */ - size_t end_pos; - - line_info (size_t l, size_t s, size_t e) - : line_num (l), start_pos (s), end_pos (e) - {} - - line_info () - :line_num (0), start_pos (0), end_pos (0) - {} - - static bool less_than(const line_info &a, const line_info &b) - { - return a.line_num < b.line_num; - } - }; - - bool needs_read_p () const; - bool needs_grow_p () const; - void maybe_grow (); - bool read_data (); - bool maybe_read_data (); - bool get_next_line (char **line, ssize_t *line_len); - bool read_next_line (char ** line, ssize_t *line_len); - bool goto_next_line (); - - static const size_t buffer_size = 4 * 1024; - static size_t line_record_size; - static size_t recent_cached_lines_shift; - - /* The number of time this file has been accessed. This is used - to designate which file cache to evict from the cache - array. */ - unsigned m_use_count; - - /* The file_path is the key for identifying a particular file in - the cache. This copy is owned by the slot. */ - char *m_file_path; - - FILE *m_fp; - - /* True when an read error happened. */ - bool m_error; - - /* This points to the content of the file that we've read so - far. */ - char *m_data; - - /* The allocated buffer to be freed may start a little earlier than DATA, - e.g. if a UTF8 BOM was skipped at the beginning. */ - int m_alloc_offset; - - /* The size of the DATA array above.*/ - size_t m_size; - - /* The number of bytes read from the underlying file so far. This - must be less (or equal) than SIZE above. */ - size_t m_nb_read; - - /* The index of the beginning of the current line. */ - size_t m_line_start_idx; - - /* The number of the previous line read. This starts at 1. Zero - means we've read no line so far. */ - size_t m_line_num; - - /* Could this file be missing a trailing newline on its final line? - Initially true (to cope with empty files), set to true/false - as each line is read. */ - bool m_missing_trailing_newline; - - /* This is a record of the beginning and end of the lines we've seen - while reading the file. This is useful to avoid walking the data - from the beginning when we are asked to read a line that is - before LINE_START_IDX above. When the lines exceed line_record_size - this is scaled down dynamically, with the line_info becoming anchors. */ - vec<line_info, va_heap> m_line_record; - - /* A cache of the recently seen lines. This is maintained as a ring - buffer. */ - vec<line_info, va_heap> m_line_recent; - - /* First and last valid entry in m_line_recent. */ - size_t m_line_recent_last, m_line_recent_first; - - void offset_buffer (int offset) - { - gcc_assert (offset < 0 ? m_alloc_offset + offset >= 0 - : (size_t) offset <= m_size); - gcc_assert (m_data); - m_alloc_offset += offset; - m_data += offset; - m_size -= offset; - } - -}; - -size_t file_cache_slot::line_record_size = 0; -size_t file_cache_slot::recent_cached_lines_shift = 8; - -/* Tune file_cache. */ -void -file_cache::tune (size_t num_file_slots, size_t lines) -{ - if (file_cache_slot::tune (lines) != lines - || m_num_file_slots != num_file_slots) - { - delete[] m_file_slots; - m_file_slots = new file_cache_slot[num_file_slots]; - } - m_num_file_slots = num_file_slots; -} - -static const char * -find_end_of_line (const char *s, size_t len); - /* Current position in real source file. */ location_t input_location = UNKNOWN_LOCATION; @@ -320,738 +139,13 @@ expand_location_1 (const line_maps *set, return xloc; } -/* Lookup the cache used for the content of a given file accessed by - caret diagnostic. Return the found cached file, or NULL if no - cached file was found. */ - -file_cache_slot * -file_cache::lookup_file (const char *file_path) -{ - gcc_assert (file_path); - - /* This will contain the found cached file. */ - file_cache_slot *r = NULL; - for (unsigned i = 0; i < m_num_file_slots; ++i) - { - file_cache_slot *c = &m_file_slots[i]; - if (c->get_file_path () && !strcmp (c->get_file_path (), file_path)) - { - c->inc_use_count (); - r = c; - } - } - - if (r) - r->inc_use_count (); - - return r; -} - -/* Purge any mention of FILENAME from the cache of files used for - printing source code. For use in selftests when working - with tempfiles. */ - -void -file_cache::forcibly_evict_file (const char *file_path) -{ - gcc_assert (file_path); - - file_cache_slot *r = lookup_file (file_path); - if (!r) - /* Not found. */ - return; - - r->evict (); -} - -/* Determine if FILE_PATH missing a trailing newline on its final line. - Only valid to call once all of the file has been loaded, by - requesting a line number beyond the end of the file. */ - -bool -file_cache::missing_trailing_newline_p (const char *file_path) -{ - gcc_assert (file_path); - - file_cache_slot *r = lookup_or_add_file (file_path); - return r->missing_trailing_newline_p (); -} - -void -file_cache::add_buffered_content (const char *file_path, - const char *buffer, - size_t sz) -{ - gcc_assert (file_path); - - file_cache_slot *r = lookup_file (file_path); - if (!r) - { - unsigned highest_use_count = 0; - r = evicted_cache_tab_entry (&highest_use_count); - if (!r->create (m_input_context, file_path, nullptr, highest_use_count)) - return; - } - - r->set_content (buffer, sz); -} - -void -file_cache_slot::evict () -{ - free (m_file_path); - m_file_path = NULL; - if (m_fp) - fclose (m_fp); - m_error = false; - m_fp = NULL; - m_nb_read = 0; - m_line_start_idx = 0; - m_line_num = 0; - m_line_record.truncate (0); - m_line_recent_first = 0; - m_line_recent_last = 0; - m_use_count = 0; - m_missing_trailing_newline = true; -} - -/* Return the file cache that has been less used, recently, or the - first empty one. If HIGHEST_USE_COUNT is non-null, - *HIGHEST_USE_COUNT is set to the highest use count of the entries - in the cache table. */ - -file_cache_slot* -file_cache::evicted_cache_tab_entry (unsigned *highest_use_count) -{ - file_cache_slot *to_evict = &m_file_slots[0]; - unsigned huc = to_evict->get_use_count (); - for (unsigned i = 1; i < m_num_file_slots; ++i) - { - file_cache_slot *c = &m_file_slots[i]; - bool c_is_empty = (c->get_file_path () == NULL); - - if (c->get_use_count () < to_evict->get_use_count () - || (to_evict->get_file_path () && c_is_empty)) - /* We evict C because it's either an entry with a lower use - count or one that is empty. */ - to_evict = c; - - if (huc < c->get_use_count ()) - huc = c->get_use_count (); - - if (c_is_empty) - /* We've reached the end of the cache; subsequent elements are - all empty. */ - break; - } - - if (highest_use_count) - *highest_use_count = huc; - - return to_evict; -} - -/* Create the cache used for the content of a given file to be - accessed by caret diagnostic. This cache is added to an array of - cache and can be retrieved by lookup_file_in_cache_tab. This - function returns the created cache. Note that only the last - m_num_file_slots files are cached. - - This can return nullptr if the FILE_PATH can't be opened for - reading, or if the content can't be converted to the input_charset. */ - -file_cache_slot* -file_cache::add_file (const char *file_path) -{ - - FILE *fp = fopen (file_path, "r"); - if (fp == NULL) - return NULL; - - unsigned highest_use_count = 0; - file_cache_slot *r = evicted_cache_tab_entry (&highest_use_count); - if (!r->create (m_input_context, file_path, fp, highest_use_count)) - return NULL; - return r; -} - -/* Get a borrowed char_span to the full content of this file - as decoded according to the input charset, encoded as UTF-8. */ - -char_span -file_cache_slot::get_full_file_content () -{ - char *line; - ssize_t line_len; - while (get_next_line (&line, &line_len)) - { - } - return char_span (m_data, m_nb_read); -} - -/* Populate this slot for use on FILE_PATH and FP, dropping any - existing cached content within it. */ - -bool -file_cache_slot::create (const file_cache::input_context &in_context, - const char *file_path, FILE *fp, - unsigned highest_use_count) -{ - m_file_path = file_path ? xstrdup (file_path) : nullptr; - if (m_fp) - fclose (m_fp); - m_error = false; - m_fp = fp; - if (m_alloc_offset) - offset_buffer (-m_alloc_offset); - m_nb_read = 0; - m_line_start_idx = 0; - m_line_num = 0; - m_line_recent_first = 0; - m_line_recent_last = 0; - m_line_record.truncate (0); - /* Ensure that this cache entry doesn't get evicted next time - add_file_to_cache_tab is called. */ - m_use_count = ++highest_use_count; - m_missing_trailing_newline = true; - - - /* Check the input configuration to determine if we need to do any - transformations, such as charset conversion or BOM skipping. */ - if (const char *input_charset = in_context.ccb (file_path)) - { - /* Need a full-blown conversion of the input charset. */ - fclose (m_fp); - m_fp = NULL; - const cpp_converted_source cs - = cpp_get_converted_source (file_path, input_charset); - if (!cs.data) - return false; - if (m_data) - XDELETEVEC (m_data); - m_data = cs.data; - m_nb_read = m_size = cs.len; - m_alloc_offset = cs.data - cs.to_free; - } - else if (in_context.should_skip_bom) - { - if (read_data ()) - { - const int offset = cpp_check_utf8_bom (m_data, m_nb_read); - offset_buffer (offset); - m_nb_read -= offset; - } - } - - return true; -} - -void -file_cache_slot::set_content (const char *buf, size_t sz) -{ - m_data = (char *)xmalloc (sz); - memcpy (m_data, buf, sz); - m_nb_read = m_size = sz; - m_alloc_offset = 0; - - if (m_fp) - { - fclose (m_fp); - m_fp = nullptr; - } -} - -/* file_cache's ctor. */ - -file_cache::file_cache () -: m_num_file_slots (16), m_file_slots (new file_cache_slot[m_num_file_slots]) -{ - initialize_input_context (nullptr, false); -} - -/* file_cache's dtor. */ - -file_cache::~file_cache () -{ - delete[] m_file_slots; -} - -void -file_cache::dump (FILE *out, int indent) const -{ - for (size_t i = 0; i < m_num_file_slots; ++i) - { - fprintf (out, "%*sslot[%i]:\n", indent, "", (int)i); - m_file_slots[i].dump (out, indent + 2); - } -} - -void -file_cache::dump () const -{ - dump (stderr, 0); -} - -/* Lookup the cache used for the content of a given file accessed by - caret diagnostic. If no cached file was found, create a new cache - for this file, add it to the array of cached file and return - it. - - This can return nullptr on a cache miss if FILE_PATH can't be opened for - reading, or if the content can't be converted to the input_charset. */ - -file_cache_slot* -file_cache::lookup_or_add_file (const char *file_path) -{ - file_cache_slot *r = lookup_file (file_path); - if (r == NULL) - r = add_file (file_path); - return r; -} - -/* Default constructor for a cache of file used by caret - diagnostic. */ - -file_cache_slot::file_cache_slot () -: m_use_count (0), m_file_path (NULL), m_fp (NULL), m_error (false), m_data (0), - m_alloc_offset (0), m_size (0), m_nb_read (0), m_line_start_idx (0), - m_line_num (0), m_missing_trailing_newline (true), - m_line_recent_last (0), m_line_recent_first (0) -{ - m_line_record.create (0); - m_line_recent.create (1U << recent_cached_lines_shift); - for (int i = 0; i < 1 << recent_cached_lines_shift; i++) - m_line_recent.quick_push (file_cache_slot::line_info (0, 0, 0)); -} - -/* Destructor for a cache of file used by caret diagnostic. */ - -file_cache_slot::~file_cache_slot () -{ - free (m_file_path); - if (m_fp) - { - fclose (m_fp); - m_fp = NULL; - } - if (m_data) - { - offset_buffer (-m_alloc_offset); - XDELETEVEC (m_data); - m_data = 0; - } - m_line_record.release (); - m_line_recent.release (); -} - -void -file_cache_slot::dump (FILE *out, int indent) const -{ - if (!m_file_path) - { - fprintf (out, "%*s(unused)\n", indent, ""); - return; - } - fprintf (out, "%*sfile_path: %s\n", indent, "", m_file_path); - fprintf (out, "%*sfp: %p\n", indent, "", (void *)m_fp); - fprintf (out, "%*sneeds_read_p: %i\n", indent, "", (int)needs_read_p ()); - fprintf (out, "%*sneeds_grow_p: %i\n", indent, "", (int)needs_grow_p ()); - fprintf (out, "%*suse_count: %i\n", indent, "", m_use_count); - fprintf (out, "%*ssize: %zi\n", indent, "", m_size); - fprintf (out, "%*snb_read: %zi\n", indent, "", m_nb_read); - fprintf (out, "%*sstart_line_idx: %zi\n", indent, "", m_line_start_idx); - fprintf (out, "%*sline_num: %zi\n", indent, "", m_line_num); - fprintf (out, "%*smissing_trailing_newline: %i\n", - indent, "", (int)m_missing_trailing_newline); - fprintf (out, "%*sline records (%i):\n", - indent, "", m_line_record.length ()); - int idx = 0; - for (auto &line : m_line_record) - fprintf (out, "%*s[%i]: line %zi: byte offsets: %zi-%zi\n", - indent + 2, "", - idx++, line.line_num, line.start_pos, line.end_pos); -} - -/* Returns TRUE iff the cache would need to be filled with data coming - from the file. That is, either the cache is empty or full or the - current line is empty. Note that if the cache is full, it would - need to be extended and filled again. */ - -bool -file_cache_slot::needs_read_p () const -{ - return m_fp && (m_nb_read == 0 - || m_nb_read == m_size - || (m_line_start_idx >= m_nb_read - 1)); -} - -/* Return TRUE iff the cache is full and thus needs to be - extended. */ - -bool -file_cache_slot::needs_grow_p () const -{ - return m_nb_read == m_size; -} - -/* Grow the cache if it needs to be extended. */ - -void -file_cache_slot::maybe_grow () -{ - if (!needs_grow_p ()) - return; - - if (!m_data) - { - gcc_assert (m_size == 0 && m_alloc_offset == 0); - m_size = buffer_size; - m_data = XNEWVEC (char, m_size); - } - else - { - const int offset = m_alloc_offset; - offset_buffer (-offset); - m_size *= 2; - m_data = XRESIZEVEC (char, m_data, m_size); - offset_buffer (offset); - } -} - -/* Read more data into the cache. Extends the cache if need be. - Returns TRUE iff new data could be read. */ - -bool -file_cache_slot::read_data () -{ - if (feof (m_fp) || ferror (m_fp)) - return false; - - maybe_grow (); - - char * from = m_data + m_nb_read; - size_t to_read = m_size - m_nb_read; - size_t nb_read = fread (from, 1, to_read, m_fp); - - if (ferror (m_fp)) - { - m_error = true; - return false; - } - - m_nb_read += nb_read; - return !!nb_read; -} - -/* Read new data iff the cache needs to be filled with more data - coming from the file FP. Return TRUE iff the cache was filled with - mode data. */ - -bool -file_cache_slot::maybe_read_data () -{ - if (!needs_read_p ()) - return false; - return read_data (); -} - -/* Helper function for file_cache_slot::get_next_line (), to find the end of - the next line. Returns with the memchr convention, i.e. nullptr if a line - terminator was not found. We need to determine line endings in the same - manner that libcpp does: any of \n, \r\n, or \r is a line ending. */ - -static const char * -find_end_of_line (const char *s, size_t len) -{ - for (const auto end = s + len; s != end; ++s) - { - if (*s == '\n') - return s; - if (*s == '\r') - { - const auto next = s + 1; - if (next == end) - { - /* Don't find the line ending if \r is the very last character - in the buffer; we do not know if it's the end of the file or - just the end of what has been read so far, and we wouldn't - want to break in the middle of what's actually a \r\n - sequence. Instead, we will handle the case of a file ending - in a \r later. */ - break; - } - return (*next == '\n' ? next : s); - } - } - return nullptr; -} - -/* Read a new line from file FP, using C as a cache for the data - coming from the file. Upon successful completion, *LINE is set to - the beginning of the line found. *LINE points directly in the - line cache and is only valid until the next call of get_next_line. - *LINE_LEN is set to the length of the line. Note that the line - does not contain any terminal delimiter. This function returns - true if some data was read or process from the cache, false - otherwise. Note that subsequent calls to get_next_line might - make the content of *LINE invalid. */ - -bool -file_cache_slot::get_next_line (char **line, ssize_t *line_len) -{ - /* Fill the cache with data to process. */ - maybe_read_data (); - - size_t remaining_size = m_nb_read - m_line_start_idx; - if (remaining_size == 0) - /* There is no more data to process. */ - return false; - - const char *line_start = m_data + m_line_start_idx; - - const char *next_line_start = NULL; - size_t len = 0; - const char *line_end = find_end_of_line (line_start, remaining_size); - if (line_end == NULL) - { - /* We haven't found an end-of-line delimiter in the cache. - Fill the cache with more data from the file and look again. */ - while (maybe_read_data ()) - { - line_start = m_data + m_line_start_idx; - remaining_size = m_nb_read - m_line_start_idx; - line_end = find_end_of_line (line_start, remaining_size); - if (line_end != NULL) - { - next_line_start = line_end + 1; - break; - } - } - if (line_end == NULL) - { - /* We've loaded all the file into the cache and still no - terminator. Let's say the line ends up at one byte past the - end of the file. This is to stay consistent with the case - of when the line ends up with a terminator and line_end points to - that. That consistency is useful below in the len calculation. - - If the file ends in a \r, we didn't identify it as a line - terminator above, so do that now instead. */ - line_end = m_data + m_nb_read; - if (m_nb_read && line_end[-1] == '\r') - { - --line_end; - m_missing_trailing_newline = false; - } - else - m_missing_trailing_newline = true; - } - else - m_missing_trailing_newline = false; - } - else - { - next_line_start = line_end + 1; - m_missing_trailing_newline = false; - } - - if (m_error) - return false; - - /* At this point, we've found the end of the of line. It either points to - the line terminator or to one byte after the last byte of the file. */ - gcc_assert (line_end != NULL); - - len = line_end - line_start; - - if (m_line_start_idx < m_nb_read) - *line = const_cast<char *> (line_start); - - ++m_line_num; - - /* Now update our line record so that re-reading lines from the - before m_line_start_idx is faster. */ - size_t rlen = m_line_record.length (); - /* Only update when beyond the previously cached region. */ - if (rlen == 0 || m_line_record[rlen - 1].line_num < m_line_num) - { - size_t spacing - = (rlen >= 2 - ? (m_line_record[rlen - 1].line_num - - m_line_record[rlen - 2].line_num) : 1); - size_t delta - = rlen >= 1 ? m_line_num - m_line_record[rlen - 1].line_num : 1; - - size_t max_size = line_record_size; - /* One anchor per hundred input lines. */ - if (max_size == 0) - max_size = m_line_num / 100; - - /* If we're too far beyond drop half of the lines to rebalance. */ - if (rlen == max_size && delta >= spacing * 2) - { - size_t j = 0; - for (size_t i = 1; i < rlen; i += 2) - m_line_record[j++] = m_line_record[i]; - m_line_record.truncate (j); - rlen = j; - spacing *= 2; - } - - if (rlen < max_size && delta >= spacing) - { - file_cache_slot::line_info li (m_line_num, m_line_start_idx, - line_end - m_data); - m_line_record.safe_push (li); - } - } - - /* Cache recent tail lines separately for fast access. This assumes - most accesses do not skip backwards. */ - if (m_line_recent_last == m_line_recent_first - || m_line_recent[m_line_recent_last].line_num == m_line_num - 1) - { - size_t mask = ((size_t) 1 << recent_cached_lines_shift) - 1; - m_line_recent_last = (m_line_recent_last + 1) & mask; - if (m_line_recent_last == m_line_recent_first) - m_line_recent_first = (m_line_recent_first + 1) & mask; - m_line_recent[m_line_recent_last] - = file_cache_slot::line_info (m_line_num, m_line_start_idx, - line_end - m_data); - } - - /* Update m_line_start_idx so that it points to the next line to be - read. */ - if (next_line_start) - m_line_start_idx = next_line_start - m_data; - else - /* We didn't find any terminal '\n'. Let's consider that the end - of line is the end of the data in the cache. The next - invocation of get_next_line will either read more data from the - underlying file or return false early because we've reached the - end of the file. */ - m_line_start_idx = m_nb_read; - - *line_len = len; - - return true; -} - -/* Consume the next bytes coming from the cache (or from its - underlying file if there are remaining unread bytes in the file) - until we reach the next end-of-line (or end-of-file). There is no - copying from the cache involved. Return TRUE upon successful - completion. */ - -bool -file_cache_slot::goto_next_line () -{ - char *l; - ssize_t len; - - return get_next_line (&l, &len); -} - -/* Read an arbitrary line number LINE_NUM from the file cached in C. - If the line was read successfully, *LINE points to the beginning - of the line in the file cache and *LINE_LEN is the length of the - line. *LINE is not nul-terminated, but may contain zero bytes. - *LINE is only valid until the next call of read_line_num. - This function returns bool if a line was read. */ - -bool -file_cache_slot::read_line_num (size_t line_num, - char ** line, ssize_t *line_len) -{ - gcc_assert (line_num > 0); - - /* Is the line in the recent line cache? - This assumes the main file processing is only using - a single contiguous cursor with only temporary excursions. */ - if (m_line_recent_first != m_line_recent_last - && m_line_recent[m_line_recent_first].line_num <= line_num - && m_line_recent[m_line_recent_last].line_num >= line_num) - { - line_info &last = m_line_recent[m_line_recent_last]; - size_t mask = (1U << recent_cached_lines_shift) - 1; - size_t idx = (m_line_recent_last - (last.line_num - line_num)) & mask; - line_info &recent = m_line_recent[idx]; - gcc_assert (recent.line_num == line_num); - *line = m_data + recent.start_pos; - *line_len = recent.end_pos - recent.start_pos; - return true; - } - - if (line_num <= m_line_num) - { - line_info l (line_num, 0, 0); - int i = m_line_record.lower_bound (l, line_info::less_than); - if (i == 0) - { - m_line_start_idx = 0; - m_line_num = 0; - } - else if (m_line_record[i - 1].line_num == line_num) - { - /* We have the start/end of the line. */ - *line = m_data + m_line_record[i - 1].start_pos; - *line_len = m_line_record[i - 1].end_pos - m_line_record[i - 1].start_pos; - return true; - } - else - { - gcc_assert (m_line_record[i - 1].line_num < m_line_num); - m_line_start_idx = m_line_record[i - 1].start_pos; - m_line_num = m_line_record[i - 1].line_num - 1; - } - } - - /* Let's walk from line m_line_num up to line_num - 1, without - copying any line. */ - while (m_line_num < line_num - 1) - if (!goto_next_line ()) - return false; - - /* The line we want is the next one. Let's read it. */ - return get_next_line (line, line_len); -} - -/* Return the physical source line that corresponds to FILE_PATH/LINE. - The line is not nul-terminated. The returned pointer is only - valid until the next call of location_get_source_line. - Note that the line can contain several null characters, - so the returned value's length has the actual length of the line. - If the function fails, a NULL char_span is returned. */ - -char_span -file_cache::get_source_line (const char *file_path, int line) -{ - char *buffer = NULL; - ssize_t len; - - if (line == 0) - return char_span (NULL, 0); - - if (file_path == NULL) - return char_span (NULL, 0); - - file_cache_slot *c = lookup_or_add_file (file_path); - if (c == NULL) - return char_span (NULL, 0); - - bool read = c->read_line_num (line, &buffer, &len); - if (!read) - return char_span (NULL, 0); - - return char_span (buffer, len); -} - /* Return a NUL-terminated copy of the source text between two locations, or NULL if the arguments are invalid. The caller is responsible for freeing the return value. */ char * -get_source_text_between (file_cache &fc, location_t start, location_t end) +get_source_text_between (diagnostics::file_cache &fc, + location_t start, location_t end) { expanded_location expstart = expand_location_to_spelling_point (start, LOCATION_ASPECT_START); @@ -1076,7 +170,8 @@ get_source_text_between (file_cache &fc, location_t start, location_t end) /* For a single line we need to trim both edges. */ if (expstart.line == expend.line) { - char_span line = fc.get_source_line (expstart.file, expstart.line); + diagnostics::char_span line + = fc.get_source_line (expstart.file, expstart.line); if (line.length () < 1) return NULL; int s = expstart.column - 1; @@ -1093,7 +188,7 @@ get_source_text_between (file_cache &fc, location_t start, location_t end) parts of the start and end lines off depending on column values. */ for (int lnum = expstart.line; lnum <= expend.line; ++lnum) { - char_span line = fc.get_source_line (expstart.file, lnum); + diagnostics::char_span line = fc.get_source_line (expstart.file, lnum); if (line.length () < 1 && (lnum != expstart.line && lnum != expend.line)) continue; @@ -1138,16 +233,6 @@ get_source_text_between (file_cache &fc, location_t start, location_t end) return xstrdup (buf); } - -char_span -file_cache::get_source_file_content (const char *file_path) -{ - file_cache_slot *c = lookup_or_add_file (file_path); - if (c == nullptr) - return char_span (nullptr, 0); - return c->get_full_file_content (); -} - /* Test if the location originates from the spelling location of a builtin-tokens. That is, return TRUE if LOC is a (possibly virtual) location of a built-in token that appears in the expansion @@ -1280,13 +365,13 @@ make_location (location_t caret, source_range src_range) source line in order to calculate the display width. If that cannot be done for any reason, then returns the byte column as a fallback. */ int -location_compute_display_column (file_cache &fc, +location_compute_display_column (diagnostics::file_cache &fc, expanded_location exploc, const cpp_char_column_policy &policy) { if (!(exploc.file && *exploc.file && exploc.line && exploc.column)) return exploc.column; - char_span line = fc.get_source_line (exploc.file, exploc.line); + diagnostics::char_span line = fc.get_source_line (exploc.file, exploc.line); /* If line is NULL, this function returns exploc.column which is the desired fallback. */ return cpp_byte_column_to_display_column (line.get_buffer (), line.length (), @@ -1431,7 +516,7 @@ dump_labelled_location_range (FILE *stream, void dump_location_info (FILE *stream) { - file_cache fc; + diagnostics::file_cache fc; /* Visualize the reserved locations. */ dump_labelled_location_range (stream, "RESERVED LOCATIONS", @@ -1506,8 +591,8 @@ dump_location_info (FILE *stream) { /* Beginning of a new source line: draw the line. */ - char_span line_text = fc.get_source_line (exploc.file, - exploc.line); + diagnostics::char_span line_text + = fc.get_source_line (exploc.file, exploc.line); if (!line_text) break; fprintf (stream, @@ -1757,7 +842,7 @@ class auto_cpp_string_vec : public auto_vec <cpp_string> static const char * get_substring_ranges_for_loc (cpp_reader *pfile, - file_cache &fc, + diagnostics::file_cache &fc, string_concat_db *concats, location_t strloc, enum cpp_ttype type, @@ -1837,7 +922,7 @@ get_substring_ranges_for_loc (cpp_reader *pfile, if (start.column > finish.column) return "range endpoints are reversed"; - char_span line = fc.get_source_line (start.file, start.line); + diagnostics::char_span line = fc.get_source_line (start.file, start.line); if (!line) return "unable to read source line"; @@ -1852,7 +937,8 @@ get_substring_ranges_for_loc (cpp_reader *pfile, if (line.length () < (start.column - 1 + literal_length)) return "line is not wide enough"; - char_span literal = line.subspan (start.column - 1, literal_length); + diagnostics::char_span literal + = line.subspan (start.column - 1, literal_length); cpp_string from; from.len = literal_length; @@ -1925,7 +1011,7 @@ get_substring_ranges_for_loc (cpp_reader *pfile, const char * get_location_within_string (cpp_reader *pfile, - file_cache &fc, + diagnostics::file_cache &fc, string_concat_db *concats, location_t strloc, enum cpp_ttype type, @@ -2008,7 +1094,7 @@ namespace selftest { static const char * get_source_range_for_char (cpp_reader *pfile, - file_cache &fc, + diagnostics::file_cache &fc, string_concat_db *concats, location_t strloc, enum cpp_ttype type, @@ -2036,7 +1122,7 @@ get_source_range_for_char (cpp_reader *pfile, static const char * get_num_source_ranges_for_substring (cpp_reader *pfile, - file_cache &fc, + diagnostics::file_cache &fc, string_concat_db *concats, location_t strloc, enum cpp_ttype type, @@ -2359,119 +1445,6 @@ test_make_location_nonpure_range_endpoints (const line_table_case &case_) ASSERT_FALSE (IS_ADHOC_LOC (get_finish (not_aaa_eq_bbb))); } -/* Verify reading of a specific line LINENUM in TMP, FC. */ - -static void -check_line (temp_source_file &tmp, file_cache &fc, int linenum) -{ - char_span line = fc.get_source_line (tmp.get_filename (), linenum); - int n; - const char *b = line.get_buffer (); - size_t l = line.length (); - char buf[5]; - ASSERT_LT (l, 5); - memcpy (buf, b, l); - buf[l] = '\0'; - ASSERT_TRUE (sscanf (buf, "%d", &n) == 1); - ASSERT_EQ (n, linenum); -} - -/* Test file cache replacement. */ - -static void -test_replacement () -{ - const int maxline = 1000; - - char *vec = XNEWVEC (char, maxline * 5); - char *p = vec; - int i; - for (i = 1; i <= maxline; i++) - p += sprintf (p, "%d\n", i); - - temp_source_file tmp (SELFTEST_LOCATION, ".txt", vec); - free (vec); - file_cache fc; - - for (i = 2; i <= maxline; i++) - { - check_line (tmp, fc, i); - check_line (tmp, fc, i - 1); - if (i >= 10) - check_line (tmp, fc, i - 9); - if (i >= 350) /* Exceed the look behind cache. */ - check_line (tmp, fc, i - 300); - } - for (i = 5; i <= maxline; i += 100) - check_line (tmp, fc, i); - for (i = 1; i <= maxline; i++) - check_line (tmp, fc, i); -} - -/* Verify reading of input files (e.g. for caret-based diagnostics). */ - -static void -test_reading_source_line () -{ - /* Create a tempfile and write some text to it. */ - temp_source_file tmp (SELFTEST_LOCATION, ".txt", - "01234567890123456789\n" - "This is the test text\n" - "This is the 3rd line"); - file_cache fc; - - /* Read back a specific line from the tempfile. */ - char_span source_line = fc.get_source_line (tmp.get_filename (), 3); - ASSERT_TRUE (source_line); - ASSERT_TRUE (source_line.get_buffer () != NULL); - ASSERT_EQ (20, source_line.length ()); - ASSERT_TRUE (!strncmp ("This is the 3rd line", - source_line.get_buffer (), source_line.length ())); - - source_line = fc.get_source_line (tmp.get_filename (), 2); - ASSERT_TRUE (source_line); - ASSERT_TRUE (source_line.get_buffer () != NULL); - ASSERT_EQ (21, source_line.length ()); - ASSERT_TRUE (!strncmp ("This is the test text", - source_line.get_buffer (), source_line.length ())); - - source_line = fc.get_source_line (tmp.get_filename (), 4); - ASSERT_FALSE (source_line); - ASSERT_TRUE (source_line.get_buffer () == NULL); -} - -/* Verify reading from buffers (e.g. for sarif-replay). */ - -static void -test_reading_source_buffer () -{ - const char *text = ("01234567890123456789\n" - "This is the test text\n" - "This is the 3rd line"); - const char *filename = "foo.txt"; - file_cache fc; - fc.add_buffered_content (filename, text, strlen (text)); - - /* Read back a specific line from the tempfile. */ - char_span source_line = fc.get_source_line (filename, 3); - ASSERT_TRUE (source_line); - ASSERT_TRUE (source_line.get_buffer () != NULL); - ASSERT_EQ (20, source_line.length ()); - ASSERT_TRUE (!strncmp ("This is the 3rd line", - source_line.get_buffer (), source_line.length ())); - - source_line = fc.get_source_line (filename, 2); - ASSERT_TRUE (source_line); - ASSERT_TRUE (source_line.get_buffer () != NULL); - ASSERT_EQ (21, source_line.length ()); - ASSERT_TRUE (!strncmp ("This is the test text", - source_line.get_buffer (), source_line.length ())); - - source_line = fc.get_source_line (filename, 4); - ASSERT_FALSE (source_line); - ASSERT_TRUE (source_line.get_buffer () == NULL); -} - /* Tests of lexing. */ /* Verify that token TOK from PARSER has cpp_token_as_text @@ -2628,7 +1601,7 @@ public: line_table_test m_ltt; cpp_reader_ptr m_parser; temp_source_file m_tempfile; - file_cache m_file_cache; + diagnostics::file_cache m_file_cache; string_concat_db m_concats; bool m_implicitly_expect_EOF; }; @@ -4356,10 +3329,6 @@ input_cc_tests () for_each_line_table_case (test_lexer_string_locations_raw_string_unterminated); for_each_line_table_case (test_lexer_char_constants); - test_reading_source_line (); - test_reading_source_buffer (); - test_replacement (); - test_line_offset_overflow (); test_cpp_utf8 (); diff --git a/gcc/input.h b/gcc/input.h index b0a1ca0f58f2..eeef290c1db8 100644 --- a/gcc/input.h +++ b/gcc/input.h @@ -23,7 +23,7 @@ along with GCC; see the file COPYING3. If not see #include "line-map.h" -class file_cache; +namespace diagnostics { class file_cache; } extern GTY(()) class line_maps *line_table; extern GTY(()) class line_maps *saved_line_table; @@ -69,110 +69,12 @@ extern expanded_location expand_location (location_t); class cpp_char_column_policy; extern int -location_compute_display_column (file_cache &fc, +location_compute_display_column (diagnostics::file_cache &fc, expanded_location exploc, const cpp_char_column_policy &policy); -/* A class capturing the bounds of a buffer, to allow for run-time - bounds-checking in a checked build. */ - -class char_span -{ - public: - char_span (const char *ptr, size_t n_elts) : m_ptr (ptr), m_n_elts (n_elts) {} - - /* Test for a non-NULL pointer. */ - operator bool() const { return m_ptr; } - - /* Get length, not including any 0-terminator (which may not be, - in fact, present). */ - size_t length () const { return m_n_elts; } - - const char *get_buffer () const { return m_ptr; } - - char operator[] (int idx) const - { - gcc_assert (idx >= 0); - gcc_assert ((size_t)idx < m_n_elts); - return m_ptr[idx]; - } - - char_span subspan (int offset, int n_elts) const - { - gcc_assert (offset >= 0); - gcc_assert (offset < (int)m_n_elts); - gcc_assert (n_elts >= 0); - gcc_assert (offset + n_elts <= (int)m_n_elts); - return char_span (m_ptr + offset, n_elts); - } - - char *xstrdup () const - { - return ::xstrndup (m_ptr, m_n_elts); - } - - private: - const char *m_ptr; - size_t m_n_elts; -}; - extern char * -get_source_text_between (file_cache &, location_t, location_t); - -/* Forward decl of slot within file_cache, so that the definition doesn't - need to be in this header. */ -class file_cache_slot; - -/* A cache of source files for use when emitting diagnostics - (and in a few places in the C/C++ frontends). - - Results are only valid until the next call to the cache, as - slots can be evicted. - - Filenames are stored by pointer, and so must outlive the cache - instance. */ - -class file_cache -{ - public: - file_cache (); - ~file_cache (); - - void dump (FILE *out, int indent) const; - void DEBUG_FUNCTION dump () const; - - file_cache_slot *lookup_or_add_file (const char *file_path); - void forcibly_evict_file (const char *file_path); - - /* See comments in diagnostic.h about the input conversion context. */ - struct input_context - { - diagnostic_input_charset_callback ccb; - bool should_skip_bom; - }; - void initialize_input_context (diagnostic_input_charset_callback ccb, - bool should_skip_bom); - - char_span get_source_file_content (const char *file_path); - char_span get_source_line (const char *file_path, int line); - bool missing_trailing_newline_p (const char *file_path); - - void add_buffered_content (const char *file_path, - const char *buffer, - size_t sz); - - void tune (size_t num_file_slots, size_t lines); - - private: - file_cache_slot *evicted_cache_tab_entry (unsigned *highest_use_count); - file_cache_slot *add_file (const char *file_path); - file_cache_slot *lookup_file (const char *file_path); - - private: - size_t m_num_file_slots; - file_cache_slot *m_file_slots; - input_context m_input_context; -}; +get_source_text_between (diagnostics::file_cache &, location_t, location_t); extern expanded_location expand_location_to_spelling_point (location_t, diff --git a/gcc/libgdiagnostics.cc b/gcc/libgdiagnostics.cc index 9ae4d210e35f..bc7ca8a195bd 100644 --- a/gcc/libgdiagnostics.cc +++ b/gcc/libgdiagnostics.cc @@ -26,6 +26,7 @@ along with GCC; see the file COPYING3. If not see #include "intl.h" #include "diagnostic.h" #include "diagnostics/color.h" +#include "diagnostics/file-cache.h" #include "diagnostics/url.h" #include "diagnostics/metadata.h" #include "diagnostics/paths.h" @@ -1358,7 +1359,7 @@ diagnostic_file::set_buffered_content (const char *buf, size_t sz) m_content = std::make_unique<content_buffer> (buf, sz); // Populate file_cache: - file_cache &fc = m_mgr.get_dc ().get_file_cache (); + diagnostics::file_cache &fc = m_mgr.get_dc ().get_file_cache (); fc.add_buffered_content (m_name.get_str (), buf, sz); } diff --git a/gcc/selftest.cc b/gcc/selftest.cc index 4fdce07c77f8..dc903407f8ed 100644 --- a/gcc/selftest.cc +++ b/gcc/selftest.cc @@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see #include "system.h" #include "coretypes.h" #include "selftest.h" +#include "diagnostics/file-cache.h" #include "intl.h" #if CHECKING_P @@ -186,7 +187,7 @@ assert_str_startswith (const location &loc, /* Constructor. Generate a name for the file. */ named_temp_file::named_temp_file (const char *suffix, - file_cache *fc) + diagnostics::file_cache *fc) { m_filename = make_temp_file (suffix); ASSERT_NE (m_filename, NULL); @@ -210,7 +211,7 @@ named_temp_file::~named_temp_file () temp_source_file::temp_source_file (const location &loc, const char *suffix, const char *content, - file_cache *fc) + diagnostics::file_cache *fc) : named_temp_file (suffix, fc) { FILE *out = fopen (get_filename (), "w"); diff --git a/gcc/selftest.h b/gcc/selftest.h index d57f3d63b5d2..4501d34181c7 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -25,7 +25,7 @@ along with GCC; see the file COPYING3. If not see #if CHECKING_P -class file_cache; +namespace diagnostics { class file_cache; } namespace selftest { @@ -100,13 +100,13 @@ extern void assert_str_startswith (const location &loc, class named_temp_file { public: - named_temp_file (const char *suffix, file_cache *fc = nullptr); + named_temp_file (const char *suffix, diagnostics::file_cache *fc = nullptr); ~named_temp_file (); const char *get_filename () const { return m_filename; } private: char *m_filename; - file_cache *m_file_cache; + diagnostics::file_cache *m_file_cache; }; /* A class for writing out a temporary sourcefile for use in selftests @@ -116,7 +116,7 @@ class temp_source_file : public named_temp_file { public: temp_source_file (const location &loc, const char *suffix, - const char *content, file_cache *fc = nullptr); + const char *content, diagnostics::file_cache *fc = nullptr); temp_source_file (const location &loc, const char *suffix, const char *content, size_t sz); }; diff --git a/gcc/substring-locations.h b/gcc/substring-locations.h index 32427dc8b8be..94139e249090 100644 --- a/gcc/substring-locations.h +++ b/gcc/substring-locations.h @@ -125,7 +125,7 @@ class format_string_diagnostic_t LANG_HOOKS_GET_SUBSTRING_LOCATION. */ extern const char *get_location_within_string (cpp_reader *pfile, - file_cache &fc, + diagnostics::file_cache &fc, string_concat_db *concats, location_t strloc, enum cpp_ttype type, diff --git a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.cc b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.cc index 5c277d71e18a..c867d463a64e 100644 --- a/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.cc +++ b/gcc/testsuite/gcc.dg/plugin/diagnostic_plugin_test_show_locus.cc @@ -63,6 +63,7 @@ #include "gcc-rich-location.h" #include "text-range-label.h" #include "diagnostics/text-sink.h" +#include "diagnostics/file-cache.h" int plugin_is_GPL_compatible; @@ -443,8 +444,8 @@ test_show_locus (function *fun) rich_location richloc (line_table, loc); for (int line = start_line; line <= finish_line; line++) { - file_cache &fc = global_dc->get_file_cache (); - char_span content = fc.get_source_line (file, line); + diagnostics::file_cache &fc = global_dc->get_file_cache (); + diagnostics::char_span content = fc.get_source_line (file, line); gcc_assert (content); /* Split line up into words. */ for (int idx = 0; idx < content.length (); idx++) @@ -464,7 +465,8 @@ test_show_locus (function *fun) richloc.add_range (word, SHOW_RANGE_WITH_CARET, &label); /* Add a fixit, converting to upper case. */ - char_span word_span = content.subspan (start_idx, idx - start_idx); + diagnostics::char_span word_span + = content.subspan (start_idx, idx - start_idx); char *copy = word_span.xstrdup (); for (char *ch = copy; *ch; ch++) *ch = TOUPPER (*ch); diff --git a/gcc/toplev.cc b/gcc/toplev.cc index e0eac813ce72..8d02f62412bb 100644 --- a/gcc/toplev.cc +++ b/gcc/toplev.cc @@ -82,6 +82,7 @@ along with GCC; see the file COPYING3. If not see #include "gcse.h" #include "omp-offload.h" #include "diagnostics/changes.h" +#include "diagnostics/file-cache.h" #include "tree-pass.h" #include "dumpfile.h" #include "ipa-fnsummary.h"