From af346c79cdd42a9853f61b0f2a9d7b50f91c1177 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 6 Jun 2017 16:59:33 -0400
Subject: [PATCH 02/24] FIXME: C/C++: temporary vec<location_t> for args at
 callsites (before blt)

---
 gcc/c-family/c-common.c                            |  38 +++--
 gcc/c-family/c-common.h                            |  10 +-
 gcc/c-family/c-format.c                            |  81 ++++++-----
 gcc/c/c-parser.c                                   |   4 +-
 gcc/c/c-tree.h                                     |   5 +
 gcc/c/c-typeck.c                                   |   2 +-
 gcc/cp/call.c                                      |  78 ++++++-----
 gcc/cp/cp-tree.h                                   |  22 +--
 gcc/cp/parser.c                                    |  26 ++--
 gcc/cp/pt.c                                        |  33 +++--
 gcc/cp/semantics.c                                 |   8 +-
 gcc/cp/typeck.c                                    |  21 +--
 gcc/gimple-ssa-sprintf.c                           |  58 ++++----
 gcc/substring-locations.c                          |  15 +-
 gcc/substring-locations.h                          |   4 +-
 .../g++.dg/diagnostic/param-type-mismatch.C        | 154 +++++++++++++++++++++
 gcc/testsuite/gcc.dg/format/diagnostic-ranges.c    |  40 +++---
 gcc/testsuite/gcc.dg/param-type-mismatch.c         |  58 ++++++++
 18 files changed, 455 insertions(+), 202 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
 create mode 100644 gcc/testsuite/gcc.dg/param-type-mismatch.c

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 0884922..08a5ab3 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -307,7 +307,8 @@ static bool check_case_bounds (location_t, tree, tree, tree *, tree *,
 			       bool *);
 
 
-static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT);
+static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT,
+			       vec<location_t> *);
 static bool nonnull_check_p (tree, unsigned HOST_WIDE_INT);
 static int resort_field_decl_cmp (const void *, const void *);
 
@@ -5141,7 +5142,8 @@ struct nonnull_arg_ctx
    warned.  */
 
 static bool
-check_function_nonnull (location_t loc, tree attrs, int nargs, tree *argarray)
+check_function_nonnull (location_t loc, tree attrs, int nargs, tree *argarray,
+			vec<location_t> *locvec)
 {
   tree a;
   int i;
@@ -5163,7 +5165,7 @@ check_function_nonnull (location_t loc, tree attrs, int nargs, tree *argarray)
   if (a != NULL_TREE)
     for (i = 0; i < nargs; i++)
       check_function_arguments_recurse (check_nonnull_arg, &ctx, argarray[i],
-					i + 1);
+					i + 1, locvec);
   else
     {
       /* Walk the argument list.  If we encounter an argument number we
@@ -5179,7 +5181,7 @@ check_function_nonnull (location_t loc, tree attrs, int nargs, tree *argarray)
 
 	  if (a != NULL_TREE)
 	    check_function_arguments_recurse (check_nonnull_arg, &ctx,
-					      argarray[i], i + 1);
+					      argarray[i], i + 1, locvec);
 	}
     }
   return ctx.warned_p;
@@ -5308,7 +5310,8 @@ nonnull_check_p (tree args, unsigned HOST_WIDE_INT param_num)
    via check_function_arguments_recurse.  */
 
 static void
-check_nonnull_arg (void *ctx, tree param, unsigned HOST_WIDE_INT param_num)
+check_nonnull_arg (void *ctx, tree param, unsigned HOST_WIDE_INT param_num,
+		   vec<location_t> */*locvec*/)
 {
   struct nonnull_arg_ctx *pctx = (struct nonnull_arg_ctx *) ctx;
 
@@ -5524,7 +5527,7 @@ attribute_fallthrough_p (tree attr)
    diagnostics.  Return true if -Wnonnull warning has been diagnosed.  */
 bool
 check_function_arguments (location_t loc, const_tree fndecl, const_tree fntype,
-			  int nargs, tree *argarray)
+			  int nargs, tree *argarray, vec<location_t> *locvec)
 {
   bool warned_p = false;
 
@@ -5533,12 +5536,13 @@ check_function_arguments (location_t loc, const_tree fndecl, const_tree fntype,
 
   if (warn_nonnull)
     warned_p = check_function_nonnull (loc, TYPE_ATTRIBUTES (fntype),
-				       nargs, argarray);
+				       nargs, argarray, locvec);
 
   /* Check for errors in format strings.  */
 
   if (warn_format || warn_suggest_attribute_format)
-    check_function_format (TYPE_ATTRIBUTES (fntype), nargs, argarray);
+    check_function_format (TYPE_ATTRIBUTES (fntype), nargs, argarray,
+			   locvec);
 
   if (warn_format)
     check_function_sentinel (fntype, nargs, argarray);
@@ -5553,9 +5557,11 @@ check_function_arguments (location_t loc, const_tree fndecl, const_tree fntype,
    once the argument is resolved.  CTX is context for the callback.  */
 void
 check_function_arguments_recurse (void (*callback)
-				  (void *, tree, unsigned HOST_WIDE_INT),
+				  (void *, tree, unsigned HOST_WIDE_INT,
+				   vec<location_t> *),
 				  void *ctx, tree param,
-				  unsigned HOST_WIDE_INT param_num)
+				  unsigned HOST_WIDE_INT param_num,
+				  vec<location_t> *locvec)
 {
   if (CONVERT_EXPR_P (param)
       && (TYPE_PRECISION (TREE_TYPE (param))
@@ -5563,7 +5569,8 @@ check_function_arguments_recurse (void (*callback)
     {
       /* Strip coercion.  */
       check_function_arguments_recurse (callback, ctx,
-					TREE_OPERAND (param, 0), param_num);
+					TREE_OPERAND (param, 0), param_num,
+					locvec);
       return;
     }
 
@@ -5600,7 +5607,8 @@ check_function_arguments_recurse (void (*callback)
 	      if (i == format_num)
 		{
 		  check_function_arguments_recurse (callback, ctx,
-						    inner_arg, param_num);
+						    inner_arg, param_num,
+						    locvec);
 		  found_format_arg = true;
 		  break;
 		}
@@ -5622,15 +5630,15 @@ check_function_arguments_recurse (void (*callback)
 	  /* Check both halves of the conditional expression.  */
 	  check_function_arguments_recurse (callback, ctx,
 					    TREE_OPERAND (param, 1),
-					    param_num);
+					    param_num, locvec);
 	  check_function_arguments_recurse (callback, ctx,
 					    TREE_OPERAND (param, 2),
-					    param_num);
+					    param_num, locvec);
 	  return;
 	}
     }
 
-  (*callback) (ctx, param, param_num);
+  (*callback) (ctx, param, param_num, locvec);
 }
 
 /* Checks for a builtin function FNDECL that the number of arguments
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 138a0a6..814d164 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -808,15 +808,17 @@ extern tree fname_decl (location_t, unsigned, tree);
 
 extern int check_user_alignment (const_tree, bool);
 extern bool check_function_arguments (location_t loc, const_tree, const_tree,
-				      int, tree *);
+				      int, tree *, vec<location_t> *);
 extern void check_function_arguments_recurse (void (*)
 					      (void *, tree,
-					       unsigned HOST_WIDE_INT),
+					       unsigned HOST_WIDE_INT,
+					       vec<location_t> *),
 					      void *, tree,
-					      unsigned HOST_WIDE_INT);
+					      unsigned HOST_WIDE_INT,
+					      vec<location_t> *);
 extern bool check_builtin_function_arguments (location_t, vec<location_t>,
 					      tree, int, tree *);
-extern void check_function_format (tree, int, tree *);
+extern void check_function_format (tree, int, tree *, vec<location_t> *);
 extern bool attribute_fallthrough_p (tree);
 extern tree handle_format_attribute (tree *, tree, tree, int, bool *);
 extern tree handle_format_arg_attribute (tree *, tree, tree, int, bool *);
diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index 400eb66..bd49645 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -92,7 +92,8 @@ format_warning_at_char (location_t fmt_string_loc, tree format_string_cst,
 
   substring_loc fmt_loc (fmt_string_loc, string_type, char_idx, char_idx,
 			 char_idx);
-  bool warned = format_warning_va (fmt_loc, NULL, NULL, opt, gmsgid, &ap);
+  bool warned = format_warning_va (fmt_loc, UNKNOWN_LOCATION, NULL, opt,
+				   gmsgid, &ap);
   va_end (ap);
 
   return warned;
@@ -977,14 +978,16 @@ format_flags (int format_num)
   gcc_unreachable ();
 }
 
-static void check_format_info (function_format_info *, tree);
-static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT);
+static void check_format_info (function_format_info *, tree, vec <location_t> *);
+static void check_format_arg (void *, tree, unsigned HOST_WIDE_INT,
+			      vec <location_t> *);
 static void check_format_info_main (format_check_results *,
 				    function_format_info *, const char *,
 				    location_t, tree,
 				    int, tree,
 				    unsigned HOST_WIDE_INT,
-				    object_allocator<format_wanted_type> &);
+				    object_allocator<format_wanted_type> &,
+				    vec<location_t> *);
 
 static void init_dollar_format_checking (int, tree);
 static int maybe_read_dollar_number (const char **, int,
@@ -999,9 +1002,10 @@ static void check_format_types (const substring_loc &fmt_loc,
 				format_wanted_type *,
 				const format_kind_info *fki,
 				int offset_to_type_start,
-				char conversion_char);
+				char conversion_char,
+				vec<location_t> *);
 static void format_type_warning (const substring_loc &fmt_loc,
-				 source_range *param_range,
+				 vec<location_t> *locvec,
 				 format_wanted_type *, tree,
 				 tree,
 				 const format_kind_info *fki,
@@ -1042,7 +1046,8 @@ decode_format_type (const char *s)
    attribute themselves.  */
 
 void
-check_function_format (tree attrs, int nargs, tree *argarray)
+check_function_format (tree attrs, int nargs, tree *argarray,
+		       vec <location_t> *locvec)
 {
   tree a;
 
@@ -1063,7 +1068,7 @@ check_function_format (tree attrs, int nargs, tree *argarray)
 	      int i;
 	      for (i = nargs - 1; i >= 0; i--)
 		params = tree_cons (NULL_TREE, argarray[i], params);
-	      check_format_info (&info, params);
+	      check_format_info (&info, params, locvec);
 	    }
 
 	  /* Attempt to detect whether the current function might benefit
@@ -1366,7 +1371,8 @@ get_flag_spec (const format_flag_spec *spec, int flag, const char *predicates)
    PARAMS is the list of argument values.  */
 
 static void
-check_format_info (function_format_info *info, tree params)
+check_format_info (function_format_info *info, tree params,
+		   vec <location_t> *locvec)
 {
   format_check_context format_ctx;
   unsigned HOST_WIDE_INT arg_num;
@@ -1402,7 +1408,7 @@ check_format_info (function_format_info *info, tree params)
   format_ctx.params = params;
 
   check_function_arguments_recurse (check_format_arg, &format_ctx,
-				    format_tree, arg_num);
+				    format_tree, arg_num, locvec);
 
   location_t loc = format_ctx.res->format_string_loc;
 
@@ -1478,7 +1484,8 @@ check_format_info (function_format_info *info, tree params)
 
 static void
 check_format_arg (void *ctx, tree format_tree,
-		  unsigned HOST_WIDE_INT arg_num)
+		  unsigned HOST_WIDE_INT arg_num,
+		  vec <location_t> *locvec)
 {
   format_check_context *format_ctx = (format_check_context *) ctx;
   format_check_results *res = format_ctx->res;
@@ -1669,7 +1676,7 @@ check_format_arg (void *ctx, tree format_tree,
   res->number_other++;
   object_allocator <format_wanted_type> fwt_pool ("format_wanted_type pool");
   check_format_info_main (res, info, format_chars, fmt_param_loc, format_tree,
-			  format_length, params, arg_num, fwt_pool);
+			  format_length, params, arg_num, fwt_pool, locvec);
 }
 
 /* Support class for argument_parser and check_format_info_main.
@@ -1733,7 +1740,8 @@ class argument_parser
 		   const char * const orig_format_chars,
 		   location_t format_string_loc, flag_chars_t &flag_chars,
 		   int &has_operand_number, tree first_fillin_param,
-		   object_allocator <format_wanted_type> &fwt_pool_);
+		   object_allocator <format_wanted_type> &fwt_pool_,
+		   vec<location_t> *locvec);
 
   bool read_any_dollar ();
 
@@ -1812,6 +1820,7 @@ class argument_parser
  private:
   format_wanted_type *first_wanted_type;
   format_wanted_type *last_wanted_type;
+  vec<location_t> *m_locvec;
 };
 
 /* flag_chars_t's constructor.  */
@@ -1933,7 +1942,8 @@ argument_parser (function_format_info *info_, const char *&format_chars_,
 		 flag_chars_t &flag_chars_,
 		 int &has_operand_number_,
 		 tree first_fillin_param_,
-		 object_allocator <format_wanted_type> &fwt_pool_)
+		 object_allocator <format_wanted_type> &fwt_pool_,
+		 vec<location_t> *locvec)
 : info (info_),
   fki (&format_types[info->format_type]),
   flag_specs (fki->flag_specs),
@@ -1949,7 +1959,8 @@ argument_parser (function_format_info *info_, const char *&format_chars_,
   has_operand_number (has_operand_number_),
   first_fillin_param (first_fillin_param_),
   first_wanted_type (NULL),
-  last_wanted_type (NULL)
+  last_wanted_type (NULL),
+  m_locvec (locvec)
 {
 }
 
@@ -2672,7 +2683,7 @@ check_argument_type (const format_char_info *fci,
       ptrdiff_t offset_to_type_start = type_start - orig_format_chars;
       check_format_types (fmt_loc, first_wanted_type, fki,
 			  offset_to_type_start,
-			  conversion_char);
+			  conversion_char, m_locvec);
     }
 
   return true;
@@ -2691,7 +2702,8 @@ check_format_info_main (format_check_results *res,
 			location_t fmt_param_loc, tree format_string_cst,
 			int format_length, tree params,
 			unsigned HOST_WIDE_INT arg_num,
-			object_allocator <format_wanted_type> &fwt_pool)
+			object_allocator <format_wanted_type> &fwt_pool,
+			vec<location_t> *locvec)
 {
   const char * const orig_format_chars = format_chars;
   const tree first_fillin_param = params;
@@ -2728,7 +2740,7 @@ check_format_info_main (format_check_results *res,
       argument_parser arg_parser (info, format_chars, format_string_cst,
 				  orig_format_chars, format_string_loc,
 				  flag_chars, has_operand_number,
-				  first_fillin_param, fwt_pool);
+				  first_fillin_param, fwt_pool, locvec);
 
       if (!arg_parser.read_any_dollar ())
 	return;
@@ -2888,7 +2900,8 @@ static void
 check_format_types (const substring_loc &fmt_loc,
 		    format_wanted_type *types, const format_kind_info *fki,
 		    int offset_to_type_start,
-		    char conversion_char)
+		    char conversion_char,
+		    vec<location_t> *locvec)
 {
   for (; types != 0; types = types->next)
     {
@@ -2915,7 +2928,7 @@ check_format_types (const substring_loc &fmt_loc,
       cur_param = types->param;
       if (!cur_param)
         {
-	  format_type_warning (fmt_loc, NULL, types, wanted_type, NULL, fki,
+	  format_type_warning (fmt_loc, locvec, types, wanted_type, NULL, fki,
 			       offset_to_type_start, conversion_char);
           continue;
         }
@@ -2926,16 +2939,6 @@ check_format_types (const substring_loc &fmt_loc,
       orig_cur_type = cur_type;
       char_type_flag = 0;
 
-      source_range param_range;
-      source_range *param_range_ptr;
-      if (CAN_HAVE_LOCATION_P (cur_param))
-	{
-	  param_range = EXPR_LOCATION_RANGE (cur_param);
-	  param_range_ptr = &param_range;
-	}
-      else
-	param_range_ptr = NULL;
-
       STRIP_NOPS (cur_param);
 
       /* Check the types of any additional pointer arguments
@@ -3000,7 +3003,7 @@ check_format_types (const substring_loc &fmt_loc,
 	    }
 	  else
 	    {
-	      format_type_warning (fmt_loc, param_range_ptr,
+	      format_type_warning (fmt_loc, locvec,
 				   types, wanted_type, orig_cur_type, fki,
 				   offset_to_type_start, conversion_char);
 	      break;
@@ -3070,7 +3073,7 @@ check_format_types (const substring_loc &fmt_loc,
 	  && TYPE_PRECISION (cur_type) == TYPE_PRECISION (wanted_type))
 	continue;
       /* Now we have a type mismatch.  */
-      format_type_warning (fmt_loc, param_range_ptr, types,
+      format_type_warning (fmt_loc, locvec, types,
 			   wanted_type, orig_cur_type, fki,
 			   offset_to_type_start, conversion_char);
     }
@@ -3402,7 +3405,7 @@ get_corrected_substring (const substring_loc &fmt_loc,
 
 static void
 format_type_warning (const substring_loc &whole_fmt_loc,
-		     source_range *param_range,
+		     vec<location_t> *locvec,
 		     format_wanted_type *type,
 		     tree wanted_type, tree arg_type,
 		     const format_kind_info *fki,
@@ -3460,11 +3463,15 @@ format_type_warning (const substring_loc &whole_fmt_loc,
     = get_corrected_substring (fmt_loc, type, arg_type, fki,
 			       offset_to_type_start, conversion_char);
 
+  location_t param_loc = UNKNOWN_LOCATION;
+  if (locvec && arg_num <= (int)locvec->length ())
+    param_loc = (*locvec)[arg_num - 1];
+
   if (wanted_type_name)
     {
       if (arg_type)
 	format_warning_at_substring
-	  (fmt_loc, param_range,
+	  (fmt_loc, param_loc,
 	   corrected_substring, OPT_Wformat_,
 	   "%s %<%s%.*s%> expects argument of type %<%s%s%>, "
 	   "but argument %d has type %qT",
@@ -3474,7 +3481,7 @@ format_type_warning (const substring_loc &whole_fmt_loc,
 	   wanted_type_name, p, arg_num, arg_type);
       else
 	format_warning_at_substring
-	  (fmt_loc, param_range,
+	  (fmt_loc, param_loc,
 	   corrected_substring, OPT_Wformat_,
 	   "%s %<%s%.*s%> expects a matching %<%s%s%> argument",
 	   gettext (kind_descriptions[kind]),
@@ -3485,7 +3492,7 @@ format_type_warning (const substring_loc &whole_fmt_loc,
     {
       if (arg_type)
 	format_warning_at_substring
-	  (fmt_loc, param_range,
+	  (fmt_loc, param_loc,
 	   corrected_substring, OPT_Wformat_,
 	   "%s %<%s%.*s%> expects argument of type %<%T%s%>, "
 	   "but argument %d has type %qT",
@@ -3495,7 +3502,7 @@ format_type_warning (const substring_loc &whole_fmt_loc,
 	   wanted_type, p, arg_num, arg_type);
       else
 	format_warning_at_substring
-	  (fmt_loc, param_range,
+	  (fmt_loc, param_loc,
 	   corrected_substring, OPT_Wformat_,
 	   "%s %<%s%.*s%> expects a matching %<%T%s%> argument",
 	   gettext (kind_descriptions[kind]),
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 9398652..0f93535 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -8702,7 +8702,7 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
   if (orig_types)
     orig_types->quick_push (expr.original_type);
   if (locations)
-    locations->safe_push (loc);
+    locations->safe_push (expr.get_location ());
   if (sizeof_arg != NULL
       && cur_sizeof_arg_loc != UNKNOWN_LOCATION
       && expr.original_code == SIZEOF_EXPR)
@@ -8730,7 +8730,7 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
       if (orig_types)
 	vec_safe_push (orig_types, expr.original_type);
       if (locations)
-	locations->safe_push (loc);
+	locations->safe_push (expr.get_location ());
       if (++idx < 3
 	  && sizeof_arg != NULL
 	  && cur_sizeof_arg_loc != UNKNOWN_LOCATION
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 5fa32a4..4ef463b 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -142,6 +142,11 @@ struct c_expr
      of this expression.  */
   location_t get_start () const { return src_range.m_start; }
   location_t get_finish () const { return src_range.m_finish; }
+  location_t get_location () const
+  {
+    return make_location (src_range.m_start, src_range.m_start,
+			  src_range.m_finish);
+  }
 
   /* Set the value to error_mark_node whilst ensuring that src_range
      is initialized.  */
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 6f9909c..49fd345 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -3111,7 +3111,7 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
 
   /* Check that the arguments to the function are valid.  */
   bool warned_p = check_function_arguments (loc, fundecl, fntype,
-					    nargs, argarray);
+					    nargs, argarray, &arg_loc);
 
   if (name != NULL_TREE
       && !strncmp (IDENTIFIER_POINTER (name), "__builtin_", 10))
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index c15b8e4..b730a85 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -145,7 +145,8 @@ static int equal_functions (tree, tree);
 static int joust (struct z_candidate *, struct z_candidate *, bool,
 		  tsubst_flags_t);
 static int compare_ics (conversion *, conversion *);
-static tree build_over_call (struct z_candidate *, int, tsubst_flags_t);
+static tree build_over_call (struct z_candidate *, int, tsubst_flags_t,
+                             vec<location_t> * = NULL);
 #define convert_like(CONV, EXPR, COMPLAIN)			\
   convert_like_real ((CONV), (EXPR), NULL_TREE, 0, 0,		\
 		     /*issue_conversion_warnings=*/true,	\
@@ -154,14 +155,16 @@ static tree build_over_call (struct z_candidate *, int, tsubst_flags_t);
   convert_like_real ((CONV), (EXPR), (FN), (ARGNO), 0,			\
 		     /*issue_conversion_warnings=*/true,		\
 		     /*c_cast_p=*/false, (COMPLAIN))
-static tree convert_like_real (conversion *, tree, tree, int, int, bool,
+static tree convert_like_real (conversion *, cp_expr, tree, int, int, bool,
 			       bool, tsubst_flags_t);
 static void op_error (location_t, enum tree_code, enum tree_code, tree,
 		      tree, tree, bool);
 static struct z_candidate *build_user_type_conversion_1 (tree, tree, int,
 							 tsubst_flags_t);
-static void print_z_candidate (location_t, const char *, struct z_candidate *);
-static void print_z_candidates (location_t, struct z_candidate *);
+static void print_z_candidate (location_t, const char *, struct z_candidate *,
+			       vec<location_t> * = NULL);
+static void print_z_candidates (location_t, struct z_candidate *,
+			        vec<location_t> * = NULL);
 static tree build_this (tree);
 static struct z_candidate *splice_viable (struct z_candidate *, bool, bool *);
 static bool any_strictly_viable (struct z_candidate *);
@@ -3443,7 +3446,8 @@ print_arity_information (location_t loc, unsigned int have, unsigned int want)
 
 static void
 print_z_candidate (location_t loc, const char *msgstr,
-		   struct z_candidate *candidate)
+		   struct z_candidate *candidate,
+		   vec<location_t> *locvec)
 {
   const char *msg = (msgstr == NULL
 		     ? ""
@@ -3532,7 +3536,7 @@ print_z_candidate (location_t loc, const char *msgstr,
 			       r->u.template_unification.return_type,
 			       r->u.template_unification.strict,
 			       r->u.template_unification.flags,
-			       true, false);
+			       true, false, locvec);
 	  break;
 	case rr_invalid_copy:
 	  inform (cloc,
@@ -3561,7 +3565,8 @@ print_z_candidate (location_t loc, const char *msgstr,
 }
 
 static void
-print_z_candidates (location_t loc, struct z_candidate *candidates)
+print_z_candidates (location_t loc, struct z_candidate *candidates,
+		    vec<location_t> *locvec)
 {
   struct z_candidate *cand1;
   struct z_candidate **cand2;
@@ -3607,7 +3612,7 @@ print_z_candidates (location_t loc, struct z_candidate *candidates)
     }
 
   for (; candidates; candidates = candidates->next)
-    print_z_candidate (loc, "candidate:", candidates);
+    print_z_candidate (loc, "candidate:", candidates, locvec);
 }
 
 /* USER_SEQ is a user-defined conversion sequence, beginning with a
@@ -4164,7 +4169,8 @@ perform_overload_resolution (tree fn,
 
 static void
 print_error_for_call_failure (tree fn, vec<tree, va_gc> *args,
-			      struct z_candidate *candidates)
+			      struct z_candidate *candidates,
+                              vec<location_t> *locvec)
 {
   tree targs = NULL_TREE;
   if (TREE_CODE (fn) == TEMPLATE_ID_EXPR)
@@ -4184,7 +4190,7 @@ print_error_for_call_failure (tree fn, vec<tree, va_gc> *args,
     error_at (loc, "call of overloaded %<%D(%A)%> is ambiguous",
 	      name, build_tree_list_vec (args));
   if (candidates)
-    print_z_candidates (loc, candidates);
+    print_z_candidates  (loc, candidates, locvec);
 }
 
 /* Return an expression for a call to FN (a namespace-scope function,
@@ -4193,7 +4199,7 @@ print_error_for_call_failure (tree fn, vec<tree, va_gc> *args,
 
 tree
 build_new_function_call (tree fn, vec<tree, va_gc> **args, bool koenig_p, 
-			 tsubst_flags_t complain)
+			 tsubst_flags_t complain, vec<location_t> *locvec)
 {
   struct z_candidate *candidates, *cand;
   bool any_viable_p;
@@ -4221,7 +4227,7 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args, bool koenig_p,
       if (!fn)
 	{
 	  if (complain & tf_error)
-	    print_error_for_call_failure (orig_fn, *args, NULL);
+	    print_error_for_call_failure (orig_fn, *args, NULL, locvec);
 	  return error_mark_node;
 	}
     }
@@ -4240,10 +4246,11 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args, bool koenig_p,
 	  // let the error be diagnosed by cp_build_function_call_vec.
 	  if (!any_viable_p && candidates && ! candidates->next
 	      && (TREE_CODE (candidates->fn) == FUNCTION_DECL))
-	    return cp_build_function_call_vec (candidates->fn, args, complain);
+	    return cp_build_function_call_vec (candidates->fn, args, complain,
+					       locvec);
 
 	  // Otherwise, emit notes for non-viable candidates.
-	  print_error_for_call_failure (fn, *args, candidates);
+	  print_error_for_call_failure (fn, *args, candidates, locvec);
 	}
       result = error_mark_node;
     }
@@ -4275,7 +4282,7 @@ build_new_function_call (tree fn, vec<tree, va_gc> **args, bool koenig_p,
           flags |= LOOKUP_EXPLICIT_TMPL_ARGS;
         }
 
-      result = build_over_call (cand, flags, complain);
+      result = build_over_call (cand, flags, complain, locvec);
     }
 
   /* Free all the conversions we allocated.  */
@@ -4356,7 +4363,7 @@ build_operator_new_call (tree fnname, vec<tree, va_gc> **args,
   if (!cand)
     {
       if (complain & tf_error)
-	print_error_for_call_failure (fns, *args, candidates);
+	print_error_for_call_failure (fns, *args, candidates, NULL);
       return error_mark_node;
     }
 
@@ -6561,14 +6568,14 @@ maybe_print_user_conv_context (conversion *convs)
    conversions to inaccessible bases are permitted.  */
 
 static tree
-convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
+convert_like_real (conversion *convs, cp_expr expr, tree fn, int argnum,
 		   int inner, bool issue_conversion_warnings,
 		   bool c_cast_p, tsubst_flags_t complain)
 {
   tree totype = convs->type;
   diagnostic_t diag_kind;
   int flags;
-  location_t loc = EXPR_LOC_OR_LOC (expr, input_location);
+  location_t loc = expr.get_location ();
 
   if (convs->bad_p && !(complain & tf_error))
     return error_mark_node;
@@ -6968,13 +6975,13 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 		   cannot create a temporary.  */
 		if (lvalue & clk_bitfield)
 		  error_at (loc, "cannot bind bitfield %qE to %qT",
-			    expr, ref_type);
+			    expr.get_value (), ref_type);
 		else if (lvalue & clk_packed)
 		  error_at (loc, "cannot bind packed field %qE to %qT",
-			    expr, ref_type);
+			    expr.get_value (), ref_type);
 		else
 		  error_at (loc, "cannot bind rvalue %qE to %qT",
-			    expr, ref_type);
+			    expr.get_value (), ref_type);
 		return error_mark_node;
 	      }
 	    /* If the source is a packed field, and we must use a copy
@@ -6988,7 +6995,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 		&& type_has_nontrivial_copy_init (type))
 	      {
 		error_at (loc, "cannot bind packed field %qE to %qT",
-			  expr, ref_type);
+			  expr.get_value (), ref_type);
 		return error_mark_node;
 	      }
 	    if (lvalue & clk_bitfield)
@@ -7523,7 +7530,8 @@ unsafe_copy_elision_p (tree target, tree exp)
    bitmask of various LOOKUP_* flags which apply to the call itself.  */
 
 static tree
-build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
+build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain,
+                 vec<location_t> *locvec)
 {
   tree fn = cand->fn;
   const vec<tree, va_gc> *args = cand->args;
@@ -7770,7 +7778,9 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
        parm = TREE_CHAIN (parm), ++arg_index, ++i)
     {
       tree type = TREE_VALUE (parm);
-      tree arg = (*args)[arg_index];
+      cp_expr arg = (*args)[arg_index];
+      if (locvec)
+        arg.set_location ((*locvec)[arg_index]);
       bool conversion_warning = true;
 
       conv = convs[i];
@@ -7914,7 +7924,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 	fargs[j] = maybe_constant_value (argarray[j]);
 
       warned_p = check_function_arguments (input_location, fn, TREE_TYPE (fn),
-					   nargs, fargs);
+					   nargs, fargs, locvec);
     }
 
   if (DECL_INHERITED_CTOR (fn))
@@ -8489,7 +8499,8 @@ name_as_c_string (tree name, tree type, bool *free_p)
 static tree
 build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
 		         tree conversion_path, int flags,
-		         tree *fn_p, tsubst_flags_t complain)
+		         tree *fn_p, tsubst_flags_t complain,
+			 vec<location_t> *locvec)
 {
   struct z_candidate *candidates = 0, *cand;
   tree explicit_targs = NULL_TREE;
@@ -8823,7 +8834,7 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
 	      if (fn_p)
 		*fn_p = fn;
 	      /* Build the actual CALL_EXPR.  */
-	      call = build_over_call (cand, flags, complain);
+	      call = build_over_call (cand, flags, complain, locvec);
 	      /* In an expression of the form `a->f()' where `f' turns
 		 out to be a static member function, `a' is
 		 none-the-less evaluated.  */
@@ -8888,12 +8899,13 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args,
 tree
 build_new_method_call (tree instance, tree fns, vec<tree, va_gc> **args,
 		       tree conversion_path, int flags,
-		       tree *fn_p, tsubst_flags_t complain)
+		       tree *fn_p, tsubst_flags_t complain,
+		       vec<location_t> *locvec)
 {
   tree ret;
   bool subtime = timevar_cond_start (TV_OVERLOAD);
   ret = build_new_method_call_1 (instance, fns, args, conversion_path, flags,
-                                 fn_p, complain);
+                                 fn_p, complain, locvec);
   timevar_cond_stop (TV_OVERLOAD, subtime);
   return ret;
 }
@@ -10071,12 +10083,12 @@ can_convert_arg_bad (tree to, tree from, tree arg, int flags,
    doing a bad conversion, convert_like will complain.  */
 
 tree
-perform_implicit_conversion_flags (tree type, tree expr,
+perform_implicit_conversion_flags (tree type, cp_expr expr,
 				   tsubst_flags_t complain, int flags)
 {
   conversion *conv;
   void *p;
-  location_t loc = EXPR_LOC_OR_LOC (expr, input_location);
+  location_t loc = expr.get_location ();
 
   if (error_operand_p (expr))
     return error_mark_node;
@@ -10392,12 +10404,12 @@ set_up_extended_ref_temp (tree decl, tree expr, vec<tree, va_gc> **cleanups,
    initializing a variable of that TYPE.  */
 
 tree
-initialize_reference (tree type, tree expr,
+initialize_reference (tree type, cp_expr expr,
 		      int flags, tsubst_flags_t complain)
 {
   conversion *conv;
   void *p;
-  location_t loc = EXPR_LOC_OR_LOC (expr, input_location);
+  location_t loc = expr.get_location ();
 
   if (type == error_mark_node || error_operand_p (expr))
     return error_mark_node;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 844f5ed..11f000d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5632,13 +5632,15 @@ extern tree extract_call_expr			(tree);
 extern tree build_user_type_conversion		(tree, tree, int,
 						 tsubst_flags_t);
 extern tree build_new_function_call		(tree, vec<tree, va_gc> **, bool, 
-						 tsubst_flags_t);
+						 tsubst_flags_t,
+						 vec<location_t> * = NULL);
 extern tree build_operator_new_call		(tree, vec<tree, va_gc> **, tree *,
 						 tree *, tree, tree, tree *,
 						 tsubst_flags_t);
 extern tree build_new_method_call		(tree, tree, vec<tree, va_gc> **,
 						 tree, int, tree *,
-						 tsubst_flags_t);
+						 tsubst_flags_t,
+						 vec<location_t> * = NULL);
 extern tree build_special_member_call		(tree, tree, vec<tree, va_gc> **,
 						 tree, int, tsubst_flags_t);
 extern tree build_new_op			(location_t, enum tree_code,
@@ -5669,7 +5671,7 @@ extern tree cxx_type_promotes_to		(tree);
 extern tree type_passed_as			(tree);
 extern tree convert_for_arg_passing		(tree, tree, tsubst_flags_t);
 extern bool is_properly_derived_from		(tree, tree);
-extern tree initialize_reference		(tree, tree, int,
+extern tree initialize_reference		(tree, cp_expr, int,
 						 tsubst_flags_t);
 extern tree extend_ref_init_temps		(tree, tree, vec<tree, va_gc>**);
 extern tree make_temporary_var_for_ref_to_temp	(tree, tree);
@@ -5678,7 +5680,8 @@ extern tree strip_top_quals			(tree);
 extern bool reference_related_p			(tree, tree);
 extern int remaining_arguments			(tree);
 extern tree perform_implicit_conversion		(tree, tree, tsubst_flags_t);
-extern tree perform_implicit_conversion_flags	(tree, tree, tsubst_flags_t, int);
+extern tree perform_implicit_conversion_flags	(tree, cp_expr, tsubst_flags_t,
+						 int);
 extern tree build_integral_nontype_arg_conv	(tree, tree, tsubst_flags_t);
 extern tree perform_direct_initialization_if_possible (tree, tree, bool,
                                                        tsubst_flags_t);
@@ -6184,7 +6187,8 @@ extern tree instantiate_template		(tree, tree, tsubst_flags_t);
 extern tree fn_type_unification			(tree, tree, tree,
 						 const tree *, unsigned int,
 						 tree, unification_kind_t, int,
-						 bool, bool);
+						 bool, bool,
+						 vec<location_t> * = NULL);
 extern void mark_decl_instantiated		(tree, int);
 extern int more_specialized_fn			(tree, tree, int);
 extern void do_decl_instantiation		(tree, tree);
@@ -6467,7 +6471,8 @@ bool empty_expr_stmt_p				(tree);
 extern cp_expr perform_koenig_lookup		(cp_expr, vec<tree, va_gc> *,
 						 tsubst_flags_t);
 extern tree finish_call_expr			(tree, vec<tree, va_gc> **, bool,
-						 bool, tsubst_flags_t);
+						 bool, tsubst_flags_t,
+						 vec<location_t> * = NULL);
 extern tree lookup_and_finish_template_variable (tree, tree, tsubst_flags_t = tf_warning_or_error);
 extern tree finish_template_variable		(tree, tsubst_flags_t = tf_warning_or_error);
 extern cp_expr finish_increment_expr		(cp_expr, enum tree_code);
@@ -6757,7 +6762,8 @@ extern tree get_member_function_from_ptrfunc	(tree *, tree, tsubst_flags_t);
 extern tree cp_build_function_call_nary         (tree, tsubst_flags_t, ...)
 						ATTRIBUTE_SENTINEL;
 extern tree cp_build_function_call_vec		(tree, vec<tree, va_gc> **,
-						 tsubst_flags_t);
+						 tsubst_flags_t,
+						 vec<location_t> * = NULL);
 extern tree build_x_binary_op			(location_t,
 						 enum tree_code, tree,
 						 enum tree_code, tree,
@@ -6797,7 +6803,7 @@ extern cp_expr build_x_modify_expr		(location_t, tree,
 extern tree cp_build_modify_expr		(location_t, tree,
 						 enum tree_code, tree,
 						 tsubst_flags_t);
-extern tree convert_for_initialization		(tree, tree, tree, int,
+extern tree convert_for_initialization		(tree, tree, cp_expr, int,
 						 impl_conv_rhs, tree, int,
                                                  tsubst_flags_t);
 extern int comp_ptr_ttypes			(tree, tree);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 7197c19..824defb 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2051,7 +2051,8 @@ static tree cp_parser_postfix_open_square_expression
 static tree cp_parser_postfix_dot_deref_expression
   (cp_parser *, enum cpp_ttype, cp_expr, bool, cp_id_kind *, location_t);
 static vec<tree, va_gc> *cp_parser_parenthesized_expression_list
-  (cp_parser *, int, bool, bool, bool *, location_t * = NULL);
+  (cp_parser *, int, bool, bool, bool *, location_t * = NULL,
+   vec<location_t> * = NULL);
 /* Values for the second parameter of cp_parser_parenthesized_expression_list.  */
 enum { non_attr = 0, normal_attr = 1, id_attr = 2 };
 static void cp_parser_pseudo_destructor_name
@@ -6835,6 +6836,7 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    bool saved_non_integral_constant_expression_p = false;
 	    tsubst_flags_t complain = complain_flags (decltype_p);
 	    vec<tree, va_gc> *args;
+	    auto_vec<location_t> locvec;
 	    location_t close_paren_loc = UNKNOWN_LOCATION;
 
             is_member_access = false;
@@ -6855,7 +6857,8 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 		    (parser, non_attr,
 		     /*cast_p=*/false, /*allow_expansion_p=*/true,
 		     /*non_constant_p=*/NULL,
-		     /*close_paren_loc=*/&close_paren_loc));
+		     /*close_paren_loc=*/&close_paren_loc,
+                     &locvec));
 	    if (is_builtin_constant_p)
 	      {
 		parser->integral_constant_expression_p
@@ -6966,14 +6969,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 			 ? LOOKUP_NORMAL|LOOKUP_NONVIRTUAL
 			 : LOOKUP_NORMAL),
 			/*fn_p=*/NULL,
-			complain));
+			complain, &locvec));
 		  }
 		else
 		  postfix_expression
 		    = finish_call_expr (postfix_expression, &args,
 					/*disallow_virtual=*/false,
 					/*koenig_p=*/false,
-					complain);
+					complain, &locvec);
 	      }
 	    else if (TREE_CODE (postfix_expression) == OFFSET_REF
 		     || TREE_CODE (postfix_expression) == MEMBER_REF
@@ -6988,14 +6991,14 @@ cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 		= finish_call_expr (postfix_expression, &args,
 				    /*disallow_virtual=*/true,
 				    koenig_p,
-				    complain);
+				    complain, &locvec);
 	    else
 	      /* All other function calls.  */
 	      postfix_expression
 		= finish_call_expr (postfix_expression, &args,
 				    /*disallow_virtual=*/false,
 				    koenig_p,
-				    complain);
+				    complain, &locvec);
 
 	    if (close_paren_loc != UNKNOWN_LOCATION)
 	      {
@@ -7511,7 +7514,8 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 					 bool cast_p,
                                          bool allow_expansion_p,
 					 bool *non_constant_p,
-					 location_t *close_paren_loc)
+					 location_t *close_paren_loc,
+					 vec<location_t> *out_locvec)
 {
   vec<tree, va_gc> *expression_list;
   bool fold_expr_p = is_attribute_list != non_attr;
@@ -7537,8 +7541,6 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
   if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_PAREN))
     while (true)
       {
-	tree expr;
-
 	/* At the beginning of attribute lists, check to see if the
 	   next token is an identifier.  */
 	if (is_attribute_list == id_attr
@@ -7555,6 +7557,8 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 	  {
 	    bool expr_non_constant_p;
 
+	    cp_expr expr;
+
 	    /* Parse the next assignment-expression.  */
 	    if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
 	      {
@@ -7596,7 +7600,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 		expressions to the list, so that we can still tell if
 		the correct form for a parenthesized expression-list
 		is found. That gives better errors.  */
-	    vec_safe_push (expression_list, expr);
+	    vec_safe_push (expression_list, expr.get_value ());
+	    if (out_locvec)
+	      out_locvec->safe_push (expr.get_location ());
 
 	    if (expr == error_mark_node)
 	      goto skip_comma;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 1567e2b..1709c30 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -154,7 +154,7 @@ static int maybe_adjust_types_for_deduction (unification_kind_t, tree*, tree*,
 static int type_unification_real (tree, tree, tree, const tree *,
 				  unsigned int, int, unification_kind_t, int,
 				  vec<deferred_access_check, va_gc> **,
-				  bool);
+				  bool, vec<location_t> * = NULL);
 static void note_template_header (int);
 static tree convert_nontype_argument_function (tree, tree, tsubst_flags_t);
 static tree convert_nontype_argument (tree, tree, tsubst_flags_t);
@@ -6251,12 +6251,14 @@ unify_too_few_arguments (bool explain_p, int have, int wanted,
 
 static int
 unify_arg_conversion (bool explain_p, tree to_type,
-		      tree from_type, tree arg)
+		      tree from_type, cp_expr arg)
 {
   if (explain_p)
-    inform (EXPR_LOC_OR_LOC (arg, input_location),
-	    "  cannot convert %qE (type %qT) to type %qT",
-	    arg, from_type, to_type);
+    {
+      inform (arg.get_location (),
+              "  cannot convert %qE (type %qT) to type %qT",
+              arg.get_value (), from_type, to_type);
+    }
   return 1;
 }
 
@@ -18339,7 +18341,8 @@ fn_type_unification (tree fn,
 		     unification_kind_t strict,
 		     int flags,
 		     bool explain_p,
-		     bool decltype_p)
+		     bool decltype_p,
+		     vec<location_t> *locvec)
 {
   tree parms;
   tree fntype;
@@ -18548,7 +18551,7 @@ fn_type_unification (tree fn,
 
   ok = !type_unification_real (DECL_INNERMOST_TEMPLATE_PARMS (fn),
 			       full_targs, parms, args, nargs, /*subr=*/0,
-			       strict, flags, &checks, explain_p);
+			       strict, flags, &checks, explain_p, locvec);
   if (!explain_p)
     pop_tinst_level ();
   if (!ok)
@@ -18783,7 +18786,7 @@ maybe_adjust_types_for_deduction (unification_kind_t strict,
    unify_one_argument.  */
 
 static int
-check_non_deducible_conversion (tree parm, tree arg, int strict,
+check_non_deducible_conversion (tree parm, cp_expr arg, int strict,
 				int flags, bool explain_p)
 {
   tree type;
@@ -18805,7 +18808,7 @@ check_non_deducible_conversion (tree parm, tree arg, int strict,
   else if (strict != DEDUCE_EXACT)
     {
       if (can_convert_arg (parm, type,
-			   TYPE_P (arg) ? NULL_TREE : arg,
+			   TYPE_P (arg) ? NULL_TREE : arg.get_value (),
 			   flags, explain_p ? tf_warning_or_error : tf_none))
 	return unify_success (explain_p);
     }
@@ -19110,9 +19113,10 @@ type_unification_real (tree tparms,
 		       unification_kind_t strict,
 		       int flags,
 		       vec<deferred_access_check, va_gc> **checks,
-		       bool explain_p)
+		       bool explain_p,
+		       vec<location_t> *locvec)
 {
-  tree parm, arg;
+  tree parm;
   int i;
   int ntparms = TREE_VEC_LENGTH (tparms);
   int saw_undeduced = 0;
@@ -19158,7 +19162,7 @@ type_unification_real (tree tparms,
 	   parameter pack is a non-deduced context.  */
 	continue;
 
-      arg = args[ia];
+      tree arg = args[ia];
       ++ia;
 
       if (unify_one_argument (tparms, full_targs, parm, arg, subr, strict,
@@ -19322,7 +19326,10 @@ type_unification_real (tree tparms,
 		 parameter pack is a non-deduced context.  */
 	      continue;
 
-	    arg = args[ia];
+	    cp_expr arg (args[ia]);
+	    if (locvec)
+	      arg.set_location ((*locvec)[ia]);
+
 	    ++ia;
 
 	    if (uses_template_parms (parm))
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 4db2462..d543605 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -2279,7 +2279,8 @@ perform_koenig_lookup (cp_expr fn, vec<tree, va_gc> *args,
 
 tree
 finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
-		  bool koenig_p, tsubst_flags_t complain)
+		  bool koenig_p, tsubst_flags_t complain,
+		  vec<location_t> *locvec)
 {
   tree result;
   tree orig_fn;
@@ -2395,7 +2396,7 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
 				       ? LOOKUP_NORMAL|LOOKUP_NONVIRTUAL
 				       : LOOKUP_NORMAL),
 				      /*fn_p=*/NULL,
-				      complain);
+				      complain, locvec);
     }
   else if (is_overloaded_fn (fn))
     {
@@ -2438,7 +2439,8 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual,
 	    }
 
 	  /* A call to a namespace-scope function.  */
-	  result = build_new_function_call (fn, args, koenig_p, complain);
+	  result = build_new_function_call (fn, args, koenig_p, complain,
+					    locvec);
 	}
     }
   else if (TREE_CODE (fn) == PSEUDO_DTOR_EXPR)
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 7aee0d6..fe7aac7 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -42,7 +42,7 @@ static tree cp_build_addr_expr_strict (tree, tsubst_flags_t);
 static tree cp_build_function_call (tree, tree, tsubst_flags_t);
 static tree pfn_from_ptrmemfunc (tree);
 static tree delta_from_ptrmemfunc (tree);
-static tree convert_for_assignment (tree, tree, impl_conv_rhs, tree, int,
+static tree convert_for_assignment (tree, cp_expr, impl_conv_rhs, tree, int,
 				    tsubst_flags_t, int);
 static tree cp_pointer_int_sum (location_t, enum tree_code, tree, tree,
 				tsubst_flags_t);
@@ -59,7 +59,7 @@ static bool maybe_warn_about_returning_address_of_local (tree);
 static tree lookup_destructor (tree, tree, tree, tsubst_flags_t);
 static void error_args_num (location_t, tree, bool);
 static int convert_arguments (tree, vec<tree, va_gc> **, tree, int,
-                              tsubst_flags_t);
+                              tsubst_flags_t, vec<location_t> *);
 
 /* Do `exp = require_complete_type (exp);' to make sure exp
    does not have an incomplete type.  (That includes void types.)
@@ -3545,7 +3545,7 @@ cp_build_function_call_nary (tree function, tsubst_flags_t complain, ...)
 
 tree
 cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
-			    tsubst_flags_t complain)
+			    tsubst_flags_t complain, vec<location_t> *locvec)
 {
   tree fntype, fndecl;
   int is_method;
@@ -3658,7 +3658,7 @@ cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
     }
 
     nargs = convert_arguments (parm_types, params, fndecl, LOOKUP_NORMAL,
-			       complain);
+			       complain, locvec);
   if (nargs < 0)
     return error_mark_node;
 
@@ -3667,7 +3667,7 @@ cp_build_function_call_vec (tree function, vec<tree, va_gc> **params,
   /* Check for errors in format strings and inappropriately
      null parameters.  */
   bool warned_p = check_function_arguments (input_location, fndecl, fntype,
-					    nargs, argarray);
+					    nargs, argarray, locvec);
 
   ret = build_cxx_call (function, nargs, argarray, complain);
 
@@ -3749,7 +3749,8 @@ error_args_num (location_t loc, tree fndecl, bool too_many_p)
 
 static int
 convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
-		   int flags, tsubst_flags_t complain)
+		   int flags, tsubst_flags_t complain,
+		   vec<location_t> *locvec)
 {
   tree typetail;
   unsigned int i;
@@ -3762,7 +3763,9 @@ convert_arguments (tree typelist, vec<tree, va_gc> **values, tree fndecl,
        i++)
     {
       tree type = typetail ? TREE_VALUE (typetail) : 0;
-      tree val = (**values)[i];
+      cp_expr val = (**values)[i];
+      if (locvec && i < locvec->length ())
+	val.set_location ((*locvec)[i]);
 
       if (val == error_mark_node || type == error_mark_node)
 	return -1;
@@ -8396,7 +8399,7 @@ delta_from_ptrmemfunc (tree t)
    argument passing, conversion in initialization, etc. */
 
 static tree
-convert_for_assignment (tree type, tree rhs,
+convert_for_assignment (tree type, cp_expr rhs,
 			impl_conv_rhs errtype, tree fndecl, int parmnum,
 			tsubst_flags_t complain, int flags)
 {
@@ -8628,7 +8631,7 @@ convert_for_assignment (tree type, tree rhs,
    exist, an ambiguity exists.  */
 
 tree
-convert_for_initialization (tree exp, tree type, tree rhs, int flags,
+convert_for_initialization (tree exp, tree type, cp_expr rhs, int flags,
 			    impl_conv_rhs errtype, tree fndecl, int parmnum,
                             tsubst_flags_t complain)
 {
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index a3153c1..eb33a87 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -579,7 +579,7 @@ get_format_string (tree format, location_t *ploc)
 /* For convenience and brevity.  */
 
 static bool
-  (* const fmtwarn) (const substring_loc &, const source_range *,
+  (* const fmtwarn) (const substring_loc &, location_t,
 		     const char *, int, const char *, ...)
   = format_warning_at_substring;
 
@@ -2414,7 +2414,7 @@ should_warn_p (const pass_sprintf_length::call_info &info,
    Return true if a warning has been issued.  */
 
 static bool
-maybe_warn (substring_loc &dirloc, source_range *pargrange,
+maybe_warn (substring_loc &dirloc, location_t param_loc,
 	    const pass_sprintf_length::call_info &info,
 	    const result_range &avail_range, const result_range &res,
 	    const directive &dir)
@@ -2472,8 +2472,8 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
 		  : G_("%qE writing a terminating nul past the end "
 		       "of the destination")));
 
-	  return fmtwarn (dirloc, NULL, NULL, info.warnopt (), fmtstr,
-			  info.func);
+	  return fmtwarn (dirloc, UNKNOWN_LOCATION, NULL, info.warnopt (),
+			  fmtstr, info.func);
 	}
 
       if (res.min == res.max)
@@ -2496,7 +2496,7 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
 			  "%wu bytes into a region of size %wu"))
 		  : G_("%<%.*s%> directive writing %wu bytes "
 		       "into a region of size %wu")));
-	  return fmtwarn (dirloc, pargrange, NULL,
+	  return fmtwarn (dirloc, param_loc, NULL,
 			  info.warnopt (), fmtstr, dir.len,
 			  target_to_host (hostdir, sizeof hostdir, dir.beg),
 			  res.min, navail);
@@ -2513,7 +2513,7 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
 		       "up to %wu bytes into a region of size %wu"))
 	       : G_("%<%.*s%> directive writing up to %wu bytes "
 		    "into a region of size %wu"));
-	  return fmtwarn (dirloc, pargrange, NULL,
+	  return fmtwarn (dirloc, param_loc, NULL,
 			  info.warnopt (), fmtstr, dir.len,
 			  target_to_host (hostdir, sizeof hostdir, dir.beg),
 			  res.max, navail);
@@ -2533,7 +2533,7 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
 		       "likely %wu or more bytes into a region of size %wu"))
 	       : G_("%<%.*s%> directive writing likely %wu or more bytes "
 		    "into a region of size %wu"));
-	  return fmtwarn (dirloc, pargrange, NULL,
+	  return fmtwarn (dirloc, param_loc, NULL,
 			  info.warnopt (), fmtstr, dir.len,
 			  target_to_host (hostdir, sizeof hostdir, dir.beg),
 			  res.likely, navail);
@@ -2550,7 +2550,7 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
 		       "between %wu and %wu bytes into a region of size %wu"))
 	       : G_("%<%.*s%> directive writing between %wu and "
 		    "%wu bytes into a region of size %wu"));
-	  return fmtwarn (dirloc, pargrange, NULL,
+	  return fmtwarn (dirloc, param_loc, NULL,
 			  info.warnopt (), fmtstr, dir.len,
 			  target_to_host (hostdir, sizeof hostdir, dir.beg),
 			  res.min, res.max, navail);
@@ -2565,7 +2565,7 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
 		   "%wu or more bytes into a region of size %wu"))
 	   : G_("%<%.*s%> directive writing %wu or more bytes "
 		"into a region of size %wu"));
-      return fmtwarn (dirloc, pargrange, NULL,
+      return fmtwarn (dirloc, param_loc, NULL,
 		      info.warnopt (), fmtstr, dir.len,
 		      target_to_host (hostdir, sizeof hostdir, dir.beg),
 		      res.min, navail);
@@ -2599,7 +2599,7 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
 	      : G_("%qE writing a terminating nul past the end "
 		   "of the destination")));
 
-      return fmtwarn (dirloc, NULL, NULL, info.warnopt (), fmtstr,
+      return fmtwarn (dirloc, UNKNOWN_LOCATION, NULL, info.warnopt (), fmtstr,
 		      info.func);
     }
 
@@ -2624,7 +2624,7 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
 	      : G_("%<%.*s%> directive writing %wu bytes "
 		   "into a region of size between %wu and %wu")));
 
-      return fmtwarn (dirloc, pargrange, NULL,
+      return fmtwarn (dirloc, param_loc, NULL,
 		      info.warnopt (), fmtstr, dir.len,
 		      target_to_host (hostdir, sizeof hostdir, dir.beg),
 		      res.min, avail_range.min, avail_range.max);
@@ -2643,7 +2643,7 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
 		   "%wu and %wu"))
 	   : G_("%<%.*s%> directive writing up to %wu bytes "
 		"into a region of size between %wu and %wu"));
-      return fmtwarn (dirloc, pargrange, NULL,
+      return fmtwarn (dirloc, param_loc, NULL,
 		      info.warnopt (), fmtstr, dir.len,
 		      target_to_host (hostdir, sizeof hostdir, dir.beg),
 		      res.max, avail_range.min, avail_range.max);
@@ -2665,7 +2665,7 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
 		   "%wu and %wu"))
 	   : G_("%<%.*s%> directive writing likely %wu or more bytes "
 		"into a region of size between %wu and %wu"));
-      return fmtwarn (dirloc, pargrange, NULL,
+      return fmtwarn (dirloc, param_loc, NULL,
 		      info.warnopt (), fmtstr, dir.len,
 		      target_to_host (hostdir, sizeof hostdir, dir.beg),
 		      res.likely, avail_range.min, avail_range.max);
@@ -2684,7 +2684,7 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
 		   "between %wu and %wu"))
 	   : G_("%<%.*s%> directive writing between %wu and "
 		"%wu bytes into a region of size between %wu and %wu"));
-      return fmtwarn (dirloc, pargrange, NULL,
+      return fmtwarn (dirloc, param_loc, NULL,
 		      info.warnopt (), fmtstr, dir.len,
 		      target_to_host (hostdir, sizeof hostdir, dir.beg),
 		      res.min, res.max, avail_range.min, avail_range.max);
@@ -2701,7 +2701,7 @@ maybe_warn (substring_loc &dirloc, source_range *pargrange,
 	       "%wu and %wu"))
        : G_("%<%.*s%> directive writing %wu or more bytes "
 	    "into a region of size between %wu and %wu"));
-  return fmtwarn (dirloc, pargrange, NULL,
+  return fmtwarn (dirloc, param_loc, NULL,
 		  info.warnopt (), fmtstr, dir.len,
 		  target_to_host (hostdir, sizeof hostdir, dir.beg),
 		  res.min, avail_range.min, avail_range.max);
@@ -2726,17 +2726,11 @@ format_directive (const pass_sprintf_length::call_info &info,
   substring_loc dirloc (info.fmtloc, TREE_TYPE (info.format),
 			offset, start, length);
 
-  /* Also create a location range for the argument if possible.
+  /* Also extract a location for the argument if possible.
      This doesn't work for integer literals or function calls.  */
-  source_range argrange;
-  source_range *pargrange;
+  location_t param_loc = UNKNOWN_LOCATION;
   if (dir.arg && CAN_HAVE_LOCATION_P (dir.arg))
-    {
-      argrange = EXPR_LOCATION_RANGE (dir.arg);
-      pargrange = &argrange;
-    }
-  else
-    pargrange = NULL;
+    param_loc = EXPR_LOCATION (dir.arg);
 
   /* Bail when there is no function to compute the output length,
      or when minimum length checking has been disabled.   */
@@ -2793,7 +2787,7 @@ format_directive (const pass_sprintf_length::call_info &info,
 
   if (fmtres.nullp)
     {
-      fmtwarn (dirloc, pargrange, NULL, info.warnopt (),
+      fmtwarn (dirloc, param_loc, NULL, info.warnopt (),
 	       "%<%.*s%> directive argument is null",
 	       dirlen, target_to_host (hostdir, sizeof hostdir, dir.beg));
 
@@ -2812,7 +2806,7 @@ format_directive (const pass_sprintf_length::call_info &info,
   bool warned = res->warned;
 
   if (!warned)
-    warned = maybe_warn (dirloc, pargrange, info, avail_range,
+    warned = maybe_warn (dirloc, param_loc, info, avail_range,
 			 fmtres.range, dir);
 
   /* Bump up the total maximum if it isn't too big.  */
@@ -2858,7 +2852,7 @@ format_directive (const pass_sprintf_length::call_info &info,
 	 (like Glibc does under some conditions).  */
 
       if (fmtres.range.min == fmtres.range.max)
-	warned = fmtwarn (dirloc, pargrange, NULL,
+	warned = fmtwarn (dirloc, param_loc, NULL,
 			  info.warnopt (),
 			  "%<%.*s%> directive output of %wu bytes exceeds "
 			  "minimum required size of 4095",
@@ -2874,7 +2868,7 @@ format_directive (const pass_sprintf_length::call_info &info,
 	       : G_("%<%.*s%> directive output between %wu and %wu "
 		    "bytes exceeds minimum required size of 4095"));
 
-	  warned = fmtwarn (dirloc, pargrange, NULL,
+	  warned = fmtwarn (dirloc, param_loc, NULL,
 			    info.warnopt (), fmtstr, dirlen,
 			    target_to_host (hostdir, sizeof hostdir, dir.beg),
 			    fmtres.range.min, fmtres.range.max);
@@ -2902,7 +2896,7 @@ format_directive (const pass_sprintf_length::call_info &info,
 	 to exceed INT_MAX bytes.  */
 
       if (fmtres.range.min == fmtres.range.max)
-	warned = fmtwarn (dirloc, pargrange, NULL, info.warnopt (),
+	warned = fmtwarn (dirloc, param_loc, NULL, info.warnopt (),
 			  "%<%.*s%> directive output of %wu bytes causes "
 			  "result to exceed %<INT_MAX%>",
 			  dirlen,
@@ -2916,7 +2910,7 @@ format_directive (const pass_sprintf_length::call_info &info,
 		     "bytes causes result to exceed %<INT_MAX%>")
 	       : G_ ("%<%.*s%> directive output between %wu and %wu "
 		     "bytes may cause result to exceed %<INT_MAX%>"));
-	  warned = fmtwarn (dirloc, pargrange, NULL,
+	  warned = fmtwarn (dirloc, param_loc, NULL,
 			    info.warnopt (), fmtstr, dirlen,
 			    target_to_host (hostdir, sizeof hostdir, dir.beg),
 			    fmtres.range.min, fmtres.range.max);
@@ -3347,7 +3341,7 @@ parse_directive (pass_sprintf_length::call_info &info,
 	  substring_loc dirloc (info.fmtloc, TREE_TYPE (info.format),
 				caret, begin, end);
 
-	  fmtwarn (dirloc, NULL, NULL,
+	  fmtwarn (dirloc, UNKNOWN_LOCATION, NULL,
 		   info.warnopt (), "%<%.*s%> directive width out of range",
 		   dir.len, target_to_host (hostdir, sizeof hostdir, dir.beg));
 	}
@@ -3381,7 +3375,7 @@ parse_directive (pass_sprintf_length::call_info &info,
 	  substring_loc dirloc (info.fmtloc, TREE_TYPE (info.format),
 				caret, begin, end);
 
-	  fmtwarn (dirloc, NULL, NULL,
+	  fmtwarn (dirloc, UNKNOWN_LOCATION, NULL,
 		   info.warnopt (), "%<%.*s%> directive precision out of range",
 		   dir.len, target_to_host (hostdir, sizeof hostdir, dir.beg));
 	}
diff --git a/gcc/substring-locations.c b/gcc/substring-locations.c
index e2d8dd7..ddba0b5 100644
--- a/gcc/substring-locations.c
+++ b/gcc/substring-locations.c
@@ -100,7 +100,7 @@ along with GCC; see the file COPYING3.  If not see
 ATTRIBUTE_GCC_DIAG (5,0)
 bool
 format_warning_va (const substring_loc &fmt_loc,
-		   const source_range *param_range,
+		   location_t param_loc,
 		   const char *corrected_substring,
 		   int opt, const char *gmsgid, va_list *ap)
 {
@@ -136,13 +136,8 @@ format_warning_va (const substring_loc &fmt_loc,
 
   rich_location richloc (line_table, primary_loc);
 
-  if (param_range)
-    {
-      location_t param_loc = make_location (param_range->m_start,
-					    param_range->m_start,
-					    param_range->m_finish);
-      richloc.add_range (param_loc, false);
-    }
+  if (param_loc != UNKNOWN_LOCATION)
+    richloc.add_range (param_loc, false);
 
   if (!err && corrected_substring && substring_within_range)
     richloc.add_fixit_replace (fmt_substring_range, corrected_substring);
@@ -171,13 +166,13 @@ format_warning_va (const substring_loc &fmt_loc,
 
 bool
 format_warning_at_substring (const substring_loc &fmt_loc,
-			     const source_range *param_range,
+			     location_t param_loc,
 			     const char *corrected_substring,
 			     int opt, const char *gmsgid, ...)
 {
   va_list ap;
   va_start (ap, gmsgid);
-  bool warned = format_warning_va (fmt_loc, param_range, corrected_substring,
+  bool warned = format_warning_va (fmt_loc, param_loc, corrected_substring,
 				   opt, gmsgid, &ap);
   va_end (ap);
 
diff --git a/gcc/substring-locations.h b/gcc/substring-locations.h
index a91cc6c..3d7796d 100644
--- a/gcc/substring-locations.h
+++ b/gcc/substring-locations.h
@@ -77,13 +77,13 @@ class substring_loc
 /* Functions for emitting a warning about a format string.  */
 
 extern bool format_warning_va (const substring_loc &fmt_loc,
-			       const source_range *param_range,
+			       location_t param_loc,
 			       const char *corrected_substring,
 			       int opt, const char *gmsgid, va_list *ap)
   ATTRIBUTE_GCC_DIAG (5,0);
 
 extern bool format_warning_at_substring (const substring_loc &fmt_loc,
-					 const source_range *param_range,
+					 location_t param_loc,
 					 const char *corrected_substring,
 					 int opt, const char *gmsgid, ...)
   ATTRIBUTE_GCC_DIAG (5,0);
diff --git a/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
new file mode 100644
index 0000000..4850d18
--- /dev/null
+++ b/gcc/testsuite/g++.dg/diagnostic/param-type-mismatch.C
@@ -0,0 +1,154 @@
+// { dg-options "-fdiagnostics-show-caret" }
+
+/* decl, with argname.  */
+
+extern int callee_1 (int one, const char *two, float three); // { dg-line callee_1 }
+
+int test_1 (int first, int second, float third)
+{
+  return callee_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return callee_1 (first, second, third);
+                           ^~~~~~
+     { dg-end-multiline-output "" } */
+  // { dg-message "initializing argument 2 of 'int callee_1\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_1 }
+  /* { dg-begin-multiline-output "" }
+ extern int callee_1 (int one, const char *two, float three);
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* decl, without argname.  */
+
+extern int callee_2 (int, const char *, float); // { dg-line callee_2 }
+
+int test_2 (int first, int second, float third)
+{
+  return callee_2 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return callee_2 (first, second, third);
+                           ^~~~~~
+     { dg-end-multiline-output "" } */
+  // { dg-message "initializing argument 2 of 'int callee_2\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_2 }
+  /* { dg-begin-multiline-output "" }
+ extern int callee_2 (int, const char *, float);
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* defn, with argname.  */
+
+static int callee_3 (int one, const char *two, float three) // { dg-line callee_3 }
+{
+  return callee_2 (one, two, three);
+}
+
+int test_3 (int first, int second, float third)
+{
+  return callee_3 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return callee_3 (first, second, third);
+                           ^~~~~~
+     { dg-end-multiline-output "" } */
+  // { dg-message "initializing argument 2 of 'int callee_3\\(int, const char\\*, float\\)'" "" { target *-*-* } callee_3 }
+  /* { dg-begin-multiline-output "" }
+ static int callee_3 (int one, const char *two, float three)
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* static member, with argname.  */
+
+struct s4 { static int member_1 (int one, const char *two, float three); };
+
+int test_4 (int first, int second, float third)
+{
+  return s4::member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return s4::member_1 (first, second, third);
+                               ^~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ struct s4 { static int member_1 (int one, const char *two, float three); };
+                        ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* non-static member, with argname.  */
+
+struct s5 { int member_1 (int one, const char *two, float three); };
+
+int test_5 (int first, int second, float third)
+{
+  s5 inst;
+  return inst.member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return inst.member_1 (first, second, third);
+                                ^~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ struct s5 { int member_1 (int one, const char *two, float three); };
+                 ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* Template function.  */
+
+template <typename T>
+int test_6 (int one, T two, float three);
+
+int test_6 (int first, int second, float third)
+{
+  return test_6 <const char *> (first, second, third); // { dg-error "no matching function" }
+  /* { dg-begin-multiline-output "" }
+   return test_6 <const char *> (first, second, third);
+                                                     ^
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   return test_6 <const char *> (first, second, third);
+                                        ^~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ int test_6 (int one, T two, float three);
+     ^~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* Template class, static function.  */
+
+template <typename T>
+struct s7 { static int member_1 (int one, T two, float three); };
+
+int test_7 (int first, int second, float third)
+{
+  return s7 <const char *>::member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return s7 <const char *>::member_1 (first, second, third);
+                                              ^~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ struct s7 { static int member_1 (int one, T two, float three); };
+                        ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* Template class, non-static function.  */
+
+template <typename T>
+struct s8 { int member_1 (int one, T two, float three); };
+
+int test_8 (int first, int second, float third)
+{
+  s8 <const char *> inst;
+  return inst.member_1 (first, second, third); // { dg-error "invalid conversion from 'int' to 'const char\\*'" }
+  /* { dg-begin-multiline-output "" }
+   return inst.member_1 (first, second, third);
+                                ^~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+ struct s8 { int member_1 (int one, T two, float three); };
+                 ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+// TODO: template callsite
diff --git a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
index cab30f2..9b3f9a5 100644
--- a/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
+++ b/gcc/testsuite/gcc.dg/format/diagnostic-ranges.c
@@ -8,28 +8,25 @@ void test_mismatching_types (const char *msg)
 {
   printf("hello %i", msg);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
 
-/* TODO: ideally would also underline "msg".  */
 /* { dg-begin-multiline-output "" }
    printf("hello %i", msg);
-                 ~^
+                 ~^   ~~~
                  %s
    { dg-end-multiline-output "" } */
 
 
   printf("hello %s", 42);  /* { dg-warning "format '%s' expects argument of type 'char \\*', but argument 2 has type 'int'" } */
-/* TODO: ideally would also underline "42".  */
 /* { dg-begin-multiline-output "" }
    printf("hello %s", 42);
-                 ~^
+                 ~^   ~~
                  %d
    { dg-end-multiline-output "" } */
 
 
   printf("hello %i", (long)0);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'long int' " } */
-/* TODO: ideally would also underline the argument.  */
 /* { dg-begin-multiline-output "" }
    printf("hello %i", (long)0);
-                 ~^
+                 ~^   ~~~~~~~
                  %li
    { dg-end-multiline-output "" } */
 }
@@ -38,11 +35,12 @@ void test_multiple_arguments (void)
 {
   printf ("arg0: %i  arg1: %s arg 2: %i", /* { dg-warning "29: format '%s'" } */
           100, 101, 102);
-/* TODO: ideally would also underline "101".  */
 /* { dg-begin-multiline-output "" }
    printf ("arg0: %i  arg1: %s arg 2: %i",
                             ~^
                             %d
+           100, 101, 102);
+                ~~~           
    { dg-end-multiline-output "" } */
 }
 
@@ -84,10 +82,9 @@ void test_hex (const char *msg)
      "i" is \x69 */
   printf("hello \x25\x69", msg);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
 
-/* TODO: ideally would also underline "msg".  */
 /* { dg-begin-multiline-output "" }
    printf("hello \x25\x69", msg);
-                 ~~~~^~~~
+                 ~~~~^~~~   ~~~
                  \x25s
    { dg-end-multiline-output "" } */
 }
@@ -98,10 +95,9 @@ void test_oct (const char *msg)
      "i" is octal 151.  */
   printf("hello \045\151", msg);  /* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
 
-/* TODO: ideally would also underline "msg".  */
 /* { dg-begin-multiline-output "" }
    printf("hello \045\151", msg);
-                 ~~~~^~~~
+                 ~~~~^~~~   ~~~
                  \045s
    { dg-end-multiline-output "" } */
 }
@@ -115,9 +111,10 @@ void test_multiple (const char *msg)
 /* { dg-begin-multiline-output "" }
    printf("prefix"  "\x25"  "\151"  "suffix",
           ^~~~~~~~
+          msg);
+          ~~~
   { dg-end-multiline-output "" } */
 
-/* TODO: ideally would also underline "msg".  */
 /* { dg-begin-multiline-output "" }
    printf("prefix"  "\x25"  "\151"  "suffix",
                      ~~~~~~~~^~~~
@@ -128,10 +125,9 @@ void test_multiple (const char *msg)
 void test_u8 (const char *msg)
 {
   printf(u8"hello %i", msg);/* { dg-warning "format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
-/* TODO: ideally would also underline "msg".  */
 /* { dg-begin-multiline-output "" }
    printf(u8"hello %i", msg);
-                   ~^
+                   ~^   ~~~
                    %s
    { dg-end-multiline-output "" } */
 }
@@ -151,7 +147,7 @@ void test_field_width_specifier (long l, int i1, int i2)
   printf (" %*.*d ", l, i1, i2); /* { dg-warning "14: field width specifier '\\*' expects argument of type 'int', but argument 2 has type 'long int'" } */
 /* { dg-begin-multiline-output "" }
    printf (" %*.*d ", l, i1, i2);
-             ~^~~~
+             ~^~~~    ~
    { dg-end-multiline-output "" } */
 }
 
@@ -160,10 +156,9 @@ void test_field_width_specifier (long l, int i1, int i2)
 void test_field_width_specifier_2 (char *d, long foo, long bar)
 {
   __builtin_sprintf (d, " %*ld ", foo, foo); /* { dg-warning "28: field width specifier '\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
-  /* TODO: ideally we'd underline the first "foo" here".  */
   /* { dg-begin-multiline-output "" }
    __builtin_sprintf (d, " %*ld ", foo, foo);
-                           ~^~~
+                           ~^~~    ~~~
    { dg-end-multiline-output "" } */
 
   __builtin_sprintf (d, " %*ld ", foo + bar, foo); /* { dg-warning "28: field width specifier '\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
@@ -176,10 +171,9 @@ void test_field_width_specifier_2 (char *d, long foo, long bar)
 void test_field_precision_specifier (char *d, long foo, long bar)
 {
   __builtin_sprintf (d, " %.*ld ", foo, foo); /* { dg-warning "29: field precision specifier '\\.\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
-  /* TODO: ideally we'd underline the first "foo" here".  */
   /* { dg-begin-multiline-output "" }
    __builtin_sprintf (d, " %.*ld ", foo, foo);
-                           ~~^~~
+                           ~~^~~    ~~~
    { dg-end-multiline-output "" } */
 
   __builtin_sprintf (d, " %.*ld ", foo + bar, foo); /* { dg-warning "29: field precision specifier '\\.\\*' expects argument of type 'int', but argument 3 has type 'long int'" } */
@@ -247,7 +241,7 @@ void test_macro (const char *msg)
   printf("hello " INT_FMT " world", msg);  /* { dg-warning "10: format '%i' expects argument of type 'int', but argument 2 has type 'const char \\*' " } */
 /* { dg-begin-multiline-output "" }
    printf("hello " INT_FMT " world", msg);
-          ^~~~~~~~
+          ^~~~~~~~                   ~~~
    { dg-end-multiline-output "" } */
 /* { dg-begin-multiline-output "" }
  #define INT_FMT "%i"
@@ -263,7 +257,7 @@ void test_macro_2 (const char *msg)
   printf("hello %" PRIu32 " world", msg);  /* { dg-warning "10: format '%u' expects argument of type 'unsigned int', but argument 2 has type 'const char \\*' " } */
 /* { dg-begin-multiline-output "" }
    printf("hello %" PRIu32 " world", msg);
-          ^~~~~~~~~
+          ^~~~~~~~~                  ~~~
    { dg-end-multiline-output "" } */
 /* { dg-begin-multiline-output "" }
  #define PRIu32 "u"
@@ -313,7 +307,7 @@ void test_non_contiguous_strings (void)
                                     /* { dg-message "26: format string is defined here" "" { target *-*-* } .-1 } */
   /* { dg-begin-multiline-output "" }
    __builtin_printf(" %" "d ", 0.5);
-                    ^~~~
+                    ^~~~       ~~~
    { dg-end-multiline-output "" } */
   /* { dg-begin-multiline-output "" }
    __builtin_printf(" %" "d ", 0.5);
@@ -330,6 +324,6 @@ void test_const_arrays (void)
   __builtin_printf(a, 0.5); /* { dg-warning "20: format .%d. expects argument of type .int., but argument 2 has type .double." } */
   /* { dg-begin-multiline-output "" }
    __builtin_printf(a, 0.5);
-                    ^
+                    ^  ~~~
    { dg-end-multiline-output "" } */
 }
diff --git a/gcc/testsuite/gcc.dg/param-type-mismatch.c b/gcc/testsuite/gcc.dg/param-type-mismatch.c
new file mode 100644
index 0000000..7a54435
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/param-type-mismatch.c
@@ -0,0 +1,58 @@
+/* { dg-options "-fdiagnostics-show-caret" }  */
+
+/* decl, with argname.  */
+
+extern int callee_1 (int one, const char *two, float three); /* { dg-line callee_1 } */
+
+int test_1 (int first, int second, float third)
+{
+  return callee_1 (first, second, third); /* { dg-warning "passing argument 2 of 'callee_1' makes pointer from integer without a cast" }  */
+  /* { dg-begin-multiline-output "" }
+   return callee_1 (first, second, third);
+                           ^~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-message "expected 'const char \\*' but argument is of type 'int'" "" { target *-*-* } callee_1 } */
+  /* { dg-begin-multiline-output "" }
+ extern int callee_1 (int one, const char *two, float three);
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* decl, without argname.  */
+
+extern int callee_2 (int, const char *, float); /* { dg-line callee_2 } */
+
+int test_2 (int first, int second, float third)
+{
+  return callee_2 (first, second, third); /* { dg-warning "passing argument 2 of 'callee_2' makes pointer from integer without a cast" } */
+  /* { dg-begin-multiline-output "" }
+   return callee_2 (first, second, third);
+                           ^~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-message "expected 'const char \\*' but argument is of type 'int'" "" { target *-*-* } callee_2 } */
+  /* { dg-begin-multiline-output "" }
+ extern int callee_2 (int, const char *, float);
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+/* defn, with argname.  */
+
+static int callee_3 (int one, const char *two, float three) /* { dg-line callee_3 } */
+{
+  return callee_2 (one, two, three);
+}
+
+int test_3 (int first, int second, float third)
+{
+  return callee_3 (first, second, third); // { dg-warning "passing argument 2 of 'callee_3' makes pointer from integer without a cast" }
+  /* { dg-begin-multiline-output "" }
+   return callee_3 (first, second, third);
+                           ^~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-message "expected 'const char \\*' but argument is of type 'int'" "" { target *-*-* } callee_3 } */
+  /* { dg-begin-multiline-output "" }
+ static int callee_3 (int one, const char *two, float three)
+            ^~~~~~~~
+     { dg-end-multiline-output "" } */
+}
-- 
1.8.5.3

