From 28faf83e9cb8204c86fffa036d61a02bfcba74d8 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 10 Oct 2018 12:51:29 -0400
Subject: [PATCH 04/18] FIXME: experiment in showing macro arguments when
 there's an bad argument count

---
 libcpp/errors.c      |  34 +++++++++--------
 libcpp/internal.h    |   6 ++-
 libcpp/macro.c       | 103 ++++++++++++++++++++++++++++++++++++++++++++++-----
 libcpp/traditional.c |   5 ++-
 4 files changed, 120 insertions(+), 28 deletions(-)

diff --git a/libcpp/errors.c b/libcpp/errors.c
index 2268fc4..b914c3f 100644
--- a/libcpp/errors.c
+++ b/libcpp/errors.c
@@ -44,33 +44,35 @@ cpp_diagnostic_at (cpp_reader * pfile, enum cpp_diagnostic_level level,
   return ret;
 }
 
-/* Print a diagnostic at the location of the previously lexed token.  */
+/* FIXME.  */
 
-ATTRIBUTE_FPTR_PRINTF(4,0)
-static bool
-cpp_diagnostic (cpp_reader * pfile, enum cpp_diagnostic_level level,
-		enum cpp_warning_reason reason,
-		const char *msgid, va_list *ap)
+source_location
+cpp_get_source_location (cpp_reader * pfile)
 {
-  source_location src_loc;
-
   if (CPP_OPTION (pfile, traditional))
     {
       if (pfile->state.in_directive)
-	src_loc = pfile->directive_line;
+	return pfile->directive_line;
       else
-	src_loc = pfile->line_table->highest_line;
+	return pfile->line_table->highest_line;
     }
   /* We don't want to refer to a token before the beginning of the
      current run -- that is invalid.  */
   else if (pfile->cur_token == pfile->cur_run->base)
-    {
-      src_loc = 0;
-    }
+    return 0;
   else
-    {
-      src_loc = pfile->cur_token[-1].src_loc;
-    }
+    return pfile->cur_token[-1].src_loc;
+}
+
+/* Print a diagnostic at the location of the previously lexed token.  */
+
+ATTRIBUTE_FPTR_PRINTF(4,0)
+static bool
+cpp_diagnostic (cpp_reader * pfile, enum cpp_diagnostic_level level,
+		enum cpp_warning_reason reason,
+		const char *msgid, va_list *ap)
+{
+  source_location src_loc = cpp_get_source_location (pfile);
   rich_location richloc (pfile->line_table, src_loc);
   return cpp_diagnostic_at (pfile, level, reason, &richloc, msgid, ap);
 }
diff --git a/libcpp/internal.h b/libcpp/internal.h
index 59f830c..b75a158 100644
--- a/libcpp/internal.h
+++ b/libcpp/internal.h
@@ -40,6 +40,7 @@ struct directive;		/* Deliberately incomplete.  */
 struct pending_option;
 struct op;
 struct _cpp_strbuf;
+struct macro_arg;
 
 typedef bool (*convert_f) (iconv_t, const unsigned char *, size_t,
 			   struct _cpp_strbuf *);
@@ -643,7 +644,7 @@ extern bool _cpp_save_parameter (cpp_reader *, unsigned, cpp_hashnode *,
 				 cpp_hashnode *);
 extern void _cpp_unsave_parameters (cpp_reader *, unsigned);
 extern bool _cpp_arguments_ok (cpp_reader *, cpp_macro *, const cpp_hashnode *,
-			       unsigned int);
+			       unsigned int, macro_arg *);
 extern const unsigned char *_cpp_builtin_macro_text (cpp_reader *,
 						     cpp_hashnode *,
 						     source_location = 0);
@@ -916,6 +917,9 @@ int linemap_get_expansion_line (struct line_maps *,
 const char* linemap_get_expansion_filename (struct line_maps *,
 					    source_location);
 
+// FIXME:
+extern source_location cpp_get_source_location (cpp_reader *pfile);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/libcpp/macro.c b/libcpp/macro.c
index aacaf8c..dfee3ad 100644
--- a/libcpp/macro.c
+++ b/libcpp/macro.c
@@ -27,7 +27,6 @@ along with this program; see the file COPYING3.  If not see
 #include "cpplib.h"
 #include "internal.h"
 
-typedef struct macro_arg macro_arg;
 /* This structure represents the tokens of a macro argument.  These
    tokens can be macro themselves, in which case they can be either
    expanded or unexpanded.  When they are expanded, this data
@@ -915,14 +914,96 @@ paste_all_tokens (cpp_reader *pfile, const cpp_token *lhs)
     _cpp_push_token_context (pfile, NULL, lhs, 1);
 }
 
+/* FIXME.  */
+
+static inline source_location
+get_start (line_maps *line_table, source_location loc)
+{
+  return get_range_from_loc (line_table, loc).m_start;
+}
+
+/* Get the endpoint of any range encoded within location LOC.  */
+
+static inline source_location
+get_finish (line_maps *line_table, source_location loc)
+{
+  return get_range_from_loc (line_table, loc).m_finish;
+}
+
+/* FIXME.  */
+
+source_location
+make_location (line_maps *line_table, source_location caret,
+	       source_location start, source_location finish)
+{
+  source_location pure_loc = get_pure_location (line_table, caret);
+  source_range src_range;
+  src_range.m_start = get_start (line_table, start);
+  src_range.m_finish = get_finish (line_table, finish);
+  source_location combined_loc = COMBINE_LOCATION_DATA (line_table,
+							pure_loc,
+							src_range,
+							NULL);
+  return combined_loc;
+}
+
+/* FIXME.  */
+
+class bad_macro_arg_count : public rich_location
+{
+ public:
+  bad_macro_arg_count (cpp_reader *pfile, cpp_macro *macro,
+		       unsigned int argc, macro_arg *args)
+  : rich_location (pfile->line_table, cpp_get_source_location (pfile)),
+    m_label (macro)
+  {
+    if (!args)
+      return;
+
+    // FIXME: min
+    for (unsigned i = 0; i < argc && i < macro->paramc; i++)
+      {
+	const macro_arg *arg = &args[i];
+	source_location start = arg->first[0]->src_loc;
+	source_location finish = arg->first[arg->count - 1]->src_loc;
+	source_location arg_loc
+	  = make_location (pfile->line_table, start, start, finish);
+	add_range (arg_loc, SHOW_RANGE_WITHOUT_CARET, &m_label);
+      }
+  }
+
+ private:
+  /* Subclass of range_label for labelling all of the various ranges.  */
+  class param_label : public range_label
+   {
+   public:
+     param_label (cpp_macro *macro) : m_macro (macro) {}
+     label_text get_text (unsigned range_idx) const FINAL OVERRIDE
+     {
+       gcc_assert (range_idx > 0);
+       unsigned param_idx = range_idx - 1;
+       // FIXME: check discrimator before accessing union
+       return label_text ((char *)m_macro->parm.params[param_idx]->ident.str,
+			  false);
+     }
+   private:
+     cpp_macro *m_macro;
+   };
+
+  /* Flyweight instance shared by all ranges.  */
+  param_label m_label;
+};
+
 /* Returns TRUE if the number of arguments ARGC supplied in an
    invocation of the MACRO referenced by NODE is valid.  An empty
    invocation to a macro with no parameters should pass ARGC as zero.
 
    Note that MACRO cannot necessarily be deduced from NODE, in case
    NODE was redefined whilst collecting arguments.  */
+// FIXME: ARGS
 bool
-_cpp_arguments_ok (cpp_reader *pfile, cpp_macro *macro, const cpp_hashnode *node, unsigned int argc)
+_cpp_arguments_ok (cpp_reader *pfile, cpp_macro *macro, const cpp_hashnode *node,
+		   unsigned int argc, macro_arg *args)
 {
   if (argc == macro->paramc)
     return true;
@@ -955,14 +1036,18 @@ _cpp_arguments_ok (cpp_reader *pfile, cpp_macro *macro, const cpp_hashnode *node
 	  return true;
 	}
 
-      cpp_error (pfile, CPP_DL_ERROR,
-		 "macro \"%s\" requires %u arguments, but only %u given",
-		 NODE_NAME (node), macro->paramc, argc);
+      bad_macro_arg_count richloc (pfile, macro, argc, args);
+      cpp_error_at (pfile, CPP_DL_ERROR, &richloc,
+		    "macro \"%s\" requires %u arguments, but only %u given",
+		    NODE_NAME (node), macro->paramc, argc);
     }
   else
-    cpp_error (pfile, CPP_DL_ERROR,
-	       "macro \"%s\" passed %u arguments, but takes just %u",
-	       NODE_NAME (node), argc, macro->paramc);
+    {
+      bad_macro_arg_count richloc (pfile, macro, argc, args);
+      cpp_error_at (pfile, CPP_DL_ERROR, &richloc,
+		    "macro \"%s\" passed %u arguments, but takes just %u",
+		    NODE_NAME (node), argc, macro->paramc);
+    }
 
   if (macro->line > RESERVED_LOCATION_COUNT)
     cpp_error_at (pfile, CPP_DL_NOTE, macro->line, "macro \"%s\" defined here",
@@ -1167,7 +1252,7 @@ collect_args (cpp_reader *pfile, const cpp_hashnode *node,
       /* A single empty argument is counted as no argument.  */
       if (argc == 1 && macro->paramc == 0 && args[0].count == 0)
 	argc = 0;
-      if (_cpp_arguments_ok (pfile, macro, node, argc))
+      if (_cpp_arguments_ok (pfile, macro, node, argc, args))
 	{
 	  /* GCC has special semantics for , ## b where b is a varargs
 	     parameter: we remove the comma if b was omitted entirely.
diff --git a/libcpp/traditional.c b/libcpp/traditional.c
index 51c3e35..9e729eb 100644
--- a/libcpp/traditional.c
+++ b/libcpp/traditional.c
@@ -636,7 +636,7 @@ _cpp_scan_out_logical_line (cpp_reader *pfile, cpp_macro *macro,
 		      memset (&m, '\0', sizeof (m));
 		      m.paramc = fmacro.paramc;
 		      if (_cpp_arguments_ok (pfile, &m, fmacro.node,
-					     fmacro.argc))
+					     fmacro.argc, NULL))
 			{
 			  size_t len = fmacro.args[1] - fmacro.args[0];
 			  uchar *buf;
@@ -719,7 +719,8 @@ _cpp_scan_out_logical_line (cpp_reader *pfile, cpp_macro *macro,
 		      && out == pfile->out.base + fmacro.offset + 1)
 		    fmacro.argc = 0;
 
-		  if (_cpp_arguments_ok (pfile, m, fmacro.node, fmacro.argc))
+		  if (_cpp_arguments_ok (pfile, m, fmacro.node, fmacro.argc,
+					 NULL))
 		    {
 		      /* Remove the macro's invocation from the
 			 output, and push its replacement text.  */
-- 
1.8.5.3

