From 4d8a24574c808f881438d65e8f333f7e152fb217 Mon Sep 17 00:00:00 2001 From: Jeff Chapman II Date: Thu, 3 Nov 2022 15:47:47 -0400 Subject: [PATCH] input: add get_source_text_between To: gcc-patches@gcc.gnu.org The c++-contracts branch uses this to retrieve the source form of the contract predicate, to be returned by contract_violation::comment(). Co-authored-by: Jason Merrill gcc/ChangeLog: * input.cc (get_source_text_between): New fn. * input.h (get_source_text_between): Declare. --- gcc/input.h | 1 + gcc/input.cc | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/gcc/input.h b/gcc/input.h index 11c571d076f..f18769950b5 100644 --- a/gcc/input.h +++ b/gcc/input.h @@ -111,6 +111,7 @@ class char_span }; extern char_span location_get_source_line (const char *file_path, int line); +extern char *get_source_text_between (location_t, location_t); extern bool location_missing_trailing_newline (const char *file_path); diff --git a/gcc/input.cc b/gcc/input.cc index a28abfac5ac..04d0809bfdf 100644 --- a/gcc/input.cc +++ b/gcc/input.cc @@ -949,6 +949,97 @@ location_get_source_line (const char *file_path, int line) return char_span (buffer, len); } +/* Return a copy of the source text between two locations. The caller is + responsible for freeing the return value. */ + +char * +get_source_text_between (location_t start, location_t end) +{ + expanded_location expstart = + expand_location_to_spelling_point (start, LOCATION_ASPECT_START); + expanded_location expend = + expand_location_to_spelling_point (end, LOCATION_ASPECT_FINISH); + + /* If the locations are in different files or the end comes before the + start, give up and return nothing. */ + if (!expstart.file || !expend.file) + return NULL; + if (strcmp (expstart.file, expend.file) != 0) + return NULL; + if (expstart.line > expend.line) + return NULL; + if (expstart.line == expend.line + && expstart.column > expend.column) + return NULL; + /* These aren't real column numbers, give up. */ + if (expstart.column == 0 || expend.column == 0) + return NULL; + + /* For a single line we need to trim both edges. */ + if (expstart.line == expend.line) + { + char_span line = location_get_source_line (expstart.file, expstart.line); + if (line.length () < 1) + return NULL; + int s = expstart.column - 1; + int len = expend.column - s; + if (line.length () < (size_t)expend.column) + return NULL; + return line.subspan (s, len).xstrdup (); + } + + struct obstack buf_obstack; + obstack_init (&buf_obstack); + + /* Loop through all lines in the range and append each to buf; may trim + parts of the start and end lines off depending on column values. */ + for (int lnum = expstart.line; lnum <= expend.line; ++lnum) + { + char_span line = location_get_source_line (expstart.file, lnum); + if (line.length () < 1 && (lnum != expstart.line && lnum != expend.line)) + continue; + + /* For the first line in the range, only start at expstart.column */ + if (lnum == expstart.line) + { + unsigned off = expstart.column - 1; + if (line.length () < off) + return NULL; + line = line.subspan (off, line.length() - off); + } + /* For the last line, don't go past expend.column */ + else if (lnum == expend.line) + { + if (line.length () < (size_t)expend.column) + return NULL; + line = line.subspan (0, expend.column); + } + + /* Combine spaces at the beginning of later lines. */ + if (lnum > expstart.line) + { + unsigned off; + for (off = 0; off < line.length(); ++off) + if (line[off] != ' ' && line[off] != '\t') + break; + if (off > 0) + { + obstack_1grow (&buf_obstack, ' '); + line = line.subspan (off, line.length() - off); + } + } + + /* This does not include any trailing newlines. */ + obstack_grow (&buf_obstack, line.get_buffer (), line.length ()); + } + + /* NUL-terminate and finish the buf obstack. */ + obstack_1grow (&buf_obstack, 0); + const char *buf = (const char *) obstack_finish (&buf_obstack); + + return xstrdup (buf); +} + /* 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. */ -- 2.31.1