From 045498e5714ab28c6869a7de4ec6283f6bf3d6f9 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 2 Aug 2017 15:27:56 -0400
Subject: [PATCH 02/22] libcpp: add linemap_position_for_file_line_and_column

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

