From 7ddf99533c7fcf694109b30b64b90b3e827af92e Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 17 Dec 2015 12:37:42 -0500
Subject: [PATCH 20/22] FIXME: WIP on a macro-aware fix for PR c/68473

---
 gcc/diagnostic-show-locus.c      | 179 +++++++++++++++++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/pr68473-2.c |  18 ++++
 gcc/testsuite/gcc.dg/pr68473-3.c |  25 ++++++
 3 files changed, 222 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/pr68473-2.c
 create mode 100644 gcc/testsuite/gcc.dg/pr68473-3.c

diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
index 75c49fb..43c8ac7 100644
--- a/gcc/diagnostic-show-locus.c
+++ b/gcc/diagnostic-show-locus.c
@@ -701,6 +701,161 @@ compatible_locations_p (location_t loc_a, location_t loc_b)
     }
 }
 
+#if 0
+/* Determine the best expanded_location to use for LOC within a
+   rich_location, given that PRIMARY_LINE_MAP contains the location
+   of that rich_location's primary caret.
+
+   A rich_location can contain multiple nested locations (e.g. for
+   expressing bogus arguments to a binary operation):
+
+       (a + b)   &&   (c + d)
+       ~~~~~~~   ^~   ~~~~~~~
+          |      |       |
+         loc1   loc0    loc2
+
+   and each nested location itself contains 3 locations: a start, caret,
+   and finish, so there could be 9 location_t values to be considered:
+
+       (a + b) &&   (c + d)
+       ^  ^  ^ ^^   ^  ^  ^
+       3  4  5 02   6  7  8
+               1
+
+   some of which could be from macro expansions.
+
+   This function handles this complexity by expanding each one of these
+   location_t values relative to the primary caret location, giving
+   an expanded_location value that class layout can print sanely.
+
+   The following cases are possible:
+
+   (A) PRIMARY_LINE_MAP is an ordinary map, LOC is an ordinary location
+
+   (B) PRIMARY_LINE_MAP is an ordinary map, LOC is a macro expansion
+   location
+
+   (C) PRIMARY_LINE_MAP is a macro map, LOC is an ordinary location
+
+   (D) PRIMARY_LINE_MAP is a macro map, LOC is a macro expansion
+   location in the same map
+
+   (E) PRIMARY_LINE_MAP is a macro map, LOC is a macro expansion
+   location in a different map
+
+   For the primary caret (which provided PRIMARY_LINE_MAP), we will
+   have either case (A) or case (D).  */
+
+static bool
+expand_sub_location (const line_map *primary_line_map, location_t loc,
+		     bool prefer_finish_p,
+		     expanded_location *out)
+{
+  /* FIXME: only expand if it's sane to do so.  */
+  // FIXME: or is it always sane?
+#if 1
+  if (linemap_macro_expansion_map_p (primary_line_map))
+    {
+      /* The primary caret is within a macro expansion;
+	 the C/C++ fronend will be printing one or more
+	   "in expansion of macro MACRO_NAME"
+	 notes, but for this location,  we'll be printing the caret
+	 and other sublocations relative to the definition point.  */
+      if (linemap_location_from_macro_expansion_p (line_table, loc))
+	{
+	  /* Cases (D) and (E).  */
+	  const line_map_macro *map
+	    = linemap_check_macro (linemap_lookup (line_table, loc));
+	  if (map == primary_line_map)
+	    {
+	      /* Case (D).  We have a pair of tokens within the same
+		 macro expansion:
+		   SOME_TOKEN   PRIMARY_TOKEN
+	                        ^
+		 or:
+		   PRIMARY_TOKEN   SOME_TOKEN
+	           ^
+		 Expand LOC within the definition location, so that we
+		 get  e.g.:
+		   SOME_TOKEN   PRIMARY_TOKEN
+	           ~~~~~~~~~~~~~^
+		 or:
+		   PRIMARY_TOKEN   SOME_TOKEN
+	           ^~~~~~~~~~~~~~~~~~~~~~~~~~
+	      */
+	      loc = linemap_resolve_location (line_table, loc,
+					      LRK_MACRO_DEFINITION_LOCATION,
+					      NULL);
+	      if (prefer_finish_p)
+		loc = get_range_from_loc (line_table, loc).m_finish;
+	      *out = linemap_client_expand_location_to_spelling_point (loc);
+	      return false;
+	    }
+	  else
+	    {
+	      /* Case (E).  */
+	      // Possibly use line-map.c's "first_map_in_common_1" for this case?
+	      gcc_unreachable (); // FIXME
+	      return false;
+	    }
+	}
+      else
+	{
+	  /* Case (C).  */
+	  gcc_unreachable (); // FIXME
+	  return false;
+	}
+    }
+  else
+    {
+      if (linemap_location_from_macro_expansion_p (line_table, loc))
+	{
+	  /* Case (B): PRIMARY_LINE_MAP is an ordinary map, LOC is a
+	     macro expansion location; we have something
+	     of the form:
+	        SOME_MACRO ()   CODE
+	                        ^
+	     or:
+	        CODE   SOME_MACRO ()
+	        ^
+	     where LOC is somewhere within a macro expansion within
+	     SOME_MACRO (possibly nested).
+
+	     The best way to print LOC the location of the SOME_MACRO expansion
+	     point, giving e.g.:
+	        SOME_MACRO ()   CODE
+	        ~~~~~~~~~~~~~~~~^
+	     or:
+	        CODE   SOME_MACRO ()
+	        ^~~~~~~~~~~~~~~~~~~~
+
+	     Hence we need to to backtrack through the expansions of LOC
+	     to find the final macro expansion site ("SOME_MACRO ()), and
+	     use its ordinary location (or that of the finish, if
+	     PREFER_FINISH_P.  */
+	  loc = linemap_resolve_location (line_table, loc,
+					  LRK_MACRO_EXPANSION_POINT,
+					  NULL);
+	  if (prefer_finish_p)
+	    loc = get_range_from_loc (line_table, loc).m_finish;
+	  *out = linemap_client_expand_location_to_spelling_point (loc);
+	  return true;
+	}
+      else
+	{
+	  /* Case (A): PRIMARY_LINE_MAP is an ordinary map, LOC is an
+	     ordinary location.  */
+	  *out = linemap_client_expand_location_to_spelling_point (loc);
+	  return true;
+	}
+    }
+#else
+  *out = linemap_client_expand_location_to_spelling_point (loc);
+  return true;
+#endif
+}
+#endif
+
 /* Implementation of class layout.  */
 
 /* Constructor for class layout.
@@ -726,6 +881,13 @@ layout::layout (diagnostic_context * context,
 {
   rich_location *richloc = diagnostic->richloc;
   source_location primary_loc = richloc->get_range (0)->m_loc;
+#if 0
+
+  /* Locate the linemap containing the caret of the primary location,
+     for use by expand_sub_location.  */
+  const line_map *primary_linemap = linemap_lookup (line_table,
+						    richloc->get_loc ());
+#endif
 
   for (unsigned int idx = 0; idx < richloc->get_num_locations (); idx++)
     {
@@ -743,6 +905,23 @@ layout::layout (diagnostic_context * context,
 	= linemap_client_expand_location_to_spelling_point (src_range.m_finish);
       expanded_location caret
 	= linemap_client_expand_location_to_spelling_point (loc_range->m_loc);
+#if 0
+      /* Split the location into caret and range information.  */
+      source_range src_range = get_range_from_loc (line_table,
+						   nested_loc->m_loc);
+
+      /* Expand the various locations.  */
+      expanded_location start, finish, caret;
+      if (!expand_sub_location (primary_linemap, src_range.m_start, false,
+				&start))
+	;/* FIXME */
+      if (!expand_sub_location (primary_linemap, src_range.m_finish, true,
+				&finish))
+	;/* FIXME */
+      if (!expand_sub_location (primary_linemap, nested_loc->m_loc, false,
+				&caret))
+	;/* FIXME */
+#endif
 
       /* If any part of the range isn't in the same file as the primary
 	 location of this diagnostic, ignore the range.  */
diff --git a/gcc/testsuite/gcc.dg/pr68473-2.c b/gcc/testsuite/gcc.dg/pr68473-2.c
new file mode 100644
index 0000000..34cd345
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr68473-2.c
@@ -0,0 +1,18 @@
+/* { dg-options "-fdiagnostics-show-caret -Wall" } */
+#define FOO(bar) bar && 42  /* { dg-warning "suggest parentheses" } */
+int param;
+int lhs;
+int f() { return lhs || FOO(param); }  /* { dg-message "in expansion of" } */
+
+/* Verify that we print the full range of the expression as it
+   pertains to the primary caret.  */
+
+/* { dg-begin-multiline-output "" }
+ #define FOO(bar) bar && 42
+                  ~~~~^~~~~
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+ int f() { return lhs || FOO(param); }
+                         ^~~
+   { dg-end-multiline-output "" } */
diff --git a/gcc/testsuite/gcc.dg/pr68473-3.c b/gcc/testsuite/gcc.dg/pr68473-3.c
new file mode 100644
index 0000000..9ed4829
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr68473-3.c
@@ -0,0 +1,25 @@
+/* From PR c/68839 (dup of 68473). */
+/* { dg-options "-fdiagnostics-show-caret -Wduplicated-cond" } */
+
+struct S { void *q; };
+
+#define NULL (void*)0
+
+void f (struct S *p)
+{
+  if (p->q != NULL) { } /* { dg-message "previously used here" } */
+    else if (p->q != NULL) { } /* { dg-warning "duplicated" } */
+}
+
+/* Verify that we print a form of the ranges as they pertain to the
+   primary caret within the location.  */
+
+/* { dg-begin-multiline-output "" }
+     else if (p->q != NULL) { }
+              ~~~~~^~~~~~~
+   { dg-end-multiline-output "" } */
+
+/* { dg-begin-multiline-output "" }
+   if (p->q != NULL) { }
+       ~~~~~^~~~~~~
+   { dg-end-multiline-output "" } */
-- 
1.8.5.3

