Tested x86_64-pc-linux-gnu, OK for trunk? -- >8 --
The c++-contracts branch uses this to retrieve the source form of the contract predicate, to be returned by contract_violation::comment(). gcc/ChangeLog: * input.cc (get_source_text_between): New fn. * input.h (get_source_text_between): Declare. --- gcc/input.h | 1 + gcc/input.cc | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 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..9b36356338a 100644 --- a/gcc/input.cc +++ b/gcc/input.cc @@ -949,6 +949,82 @@ 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, abort 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; + + /* 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 l = expend.column - s; + if (line.length () < (size_t)expend.column) + return NULL; + return line.subspan (s, l).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 l = expstart.line; l <= expend.line; ++l) + { + char_span line = location_get_source_line (expstart.file, l); + if (line.length () < 1 && (l != expstart.line && l != expend.line)) + continue; + + /* For the first line in the range, only start at expstart.column */ + if (l == expstart.line) + { + if (expstart.column == 0) + return NULL; + if (line.length () < (size_t)expstart.column - 1) + return NULL; + line = line.subspan (expstart.column - 1, + line.length() - expstart.column + 1); + } + /* For the last line, don't go past expend.column */ + else if (l == expend.line) + { + if (line.length () < (size_t)expend.column) + return NULL; + line = line.subspan (0, expend.column); + } + + 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); + + /* TODO should we collapse/trim newlines and runs of spaces? */ + 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. */ base-commit: a4cd2389276a30c39034a83d640ce68fa407bac1 prerequisite-patch-id: 329bc16a88dc9a3b13cd3fcecb3678826cc592dc prerequisite-patch-id: 49e922c10f6da687d9da3f6a0fd20f324bd352d6 -- 2.31.1