gcc/ChangeLog:
        * input.c (selftest::test_making_arbitrary_locations): New function.
        (selftest::input_c_tests): Call it.

libcpp/ChangeLog:
        * include/line-map.h (linemap_position_for_file_line_and_column):
        New decl.
        * line-map.c (linemap_position_for_file_line_and_column): New
        function.
---
 gcc/input.c               | 32 +++++++++++++++++++++++++++++
 libcpp/include/line-map.h |  9 +++++++++
 libcpp/line-map.c         | 51 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 92 insertions(+)

diff --git a/gcc/input.c b/gcc/input.c
index 1aad551..a3a8454 100644
--- a/gcc/input.c
+++ b/gcc/input.c
@@ -1795,6 +1795,37 @@ test_accessing_ordinary_linemaps (const line_table_case 
&case_)
   ASSERT_EQ (loc_d, src_range.m_finish);
 }
 
+/* Verify that linemap_position_for_file_line_and_column works.  */
+
+static void
+test_making_arbitrary_locations (const line_table_case &case_)
+{
+  line_table_test ltt (case_);
+
+  /* Verify that we can make various locations in arbitrary order,
+     sometimes changing file, sometimes going back and making a
+     location "earlier" than ones we've created before.  */
+
+  ASSERT_LOCEQ ("foo.c", 10, 5,
+               linemap_position_for_file_line_and_column (line_table,
+                                                          "foo.c", 10, 5));
+  ASSERT_LOCEQ ("foo.c", 10, 6,
+               linemap_position_for_file_line_and_column (line_table,
+                                                          "foo.c", 10, 6));
+  ASSERT_LOCEQ ("foo.c", 20, 1,
+               linemap_position_for_file_line_and_column (line_table,
+                                                          "foo.c", 20, 1));
+  ASSERT_LOCEQ ("bar.c", 100, 12,
+               linemap_position_for_file_line_and_column (line_table,
+                                                          "bar.c", 100, 12));
+  ASSERT_LOCEQ ("foo.c", 30, 1,
+               linemap_position_for_file_line_and_column (line_table,
+                                                          "foo.c", 30, 1));
+  ASSERT_LOCEQ ("foo.c", 15, 1,
+               linemap_position_for_file_line_and_column (line_table,
+                                                          "foo.c", 15, 1));
+}
+
 /* Verify various properties of UNKNOWN_LOCATION.  */
 
 static void
@@ -3528,6 +3559,7 @@ input_c_tests ()
   for_each_line_table_case (test_make_location_nonpure_range_endpoints);
 
   for_each_line_table_case (test_accessing_ordinary_linemaps);
+  for_each_line_table_case (test_making_arbitrary_locations);
   for_each_line_table_case (test_lexer);
   for_each_line_table_case (test_lexer_string_locations_simple);
   for_each_line_table_case (test_lexer_string_locations_ebcdic);
diff --git a/libcpp/include/line-map.h b/libcpp/include/line-map.h
index e696041..3c74bb0 100644
--- a/libcpp/include/line-map.h
+++ b/libcpp/include/line-map.h
@@ -1192,6 +1192,15 @@ linemap_position_for_loc_and_offset (struct line_maps 
*set,
                                     source_location loc,
                                     unsigned int offset);
 
+/* Encode and return a source location from a given file, line and column.
+   This is much less efficient than the above functions, and should only
+   be used as a last resort.  */
+
+source_location
+linemap_position_for_file_line_and_column (struct line_maps *set,
+                                          const char *, linenum_type,
+                                          unsigned int);
+
 /* Return the file this map is for.  */
 inline const char *
 LINEMAP_FILE (const line_map_ordinary *ord_map)
diff --git a/libcpp/line-map.c b/libcpp/line-map.c
index 0e5804b..32294f5 100644
--- a/libcpp/line-map.c
+++ b/libcpp/line-map.c
@@ -935,6 +935,57 @@ linemap_position_for_loc_and_offset (struct line_maps *set,
   return r;
 }
 
+/* Encode and return a source location from a given file, line and column.
+   This is much less efficient than the above functions, and should only
+   be used as a last resort.  */
+
+source_location
+linemap_position_for_file_line_and_column (struct line_maps *set,
+                                          const char *file, linenum_type line,
+                                          unsigned int column)
+{
+  /* First, attempt to find a pre-existing linemap that can represent
+     the location.  */
+  for (unsigned int i = 0; i < LINEMAPS_ORDINARY_USED (set); i++)
+    {
+      line_map_ordinary *ord_map = LINEMAPS_ORDINARY_MAP_AT (set, i);
+      if (0 == strcmp (file, ord_map->to_file))
+       {
+         source_location loc
+           = linemap_position_for_line_and_column (set, ord_map,
+                                                   line, column);
+         /* Check that it's a valid location within ord_map.  */
+         if (i + 1 < LINEMAPS_ORDINARY_USED (set))
+           {
+             line_map_ordinary *next_ord_map
+               = LINEMAPS_ORDINARY_MAP_AT (set, i + 1);
+             if (loc >= next_ord_map->start_location)
+               continue;
+           }
+
+         return loc;
+       }
+    }
+
+  /* Failing that, we need a new linemap.  */
+  const line_map_ordinary *ord_map
+    = linemap_check_ordinary (linemap_add
+                             (set, LC_ENTER,
+                              /* Assume that it's not in a system header.  */
+                              false,
+                              xstrdup (file), line));
+  unsigned int max_column_hint = MAX (80, column * 2);
+  linemap_line_start (set, line, max_column_hint);
+
+  source_location loc
+    = linemap_position_for_line_and_column (set, ord_map,
+                                           line, column);
+
+  linemap_add (set, LC_LEAVE, false, NULL, 0);
+
+  return loc;
+}
+
 /* Given a virtual source location yielded by a map (either an
    ordinary or a macro map), returns that map.  */
 
-- 
1.8.5.3

Reply via email to