From 7cb41e6c23c4f052c94b1b1d5950b500e6ab9205 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Thu, 11 May 2017 15:00:44 -0400
Subject: [PATCH 05/14] FIXME: start generalizing to C (seems to break cc1plus,
 with GTY issues)

---
 gcc/ast.c                                  |   5 +
 gcc/ast.def                                |   4 +
 gcc/c-family/c-common.h                    |   3 +-
 gcc/c-family/c.opt                         |   4 +-
 gcc/c/c-decl.c                             |  13 ++-
 gcc/c/c-parser.c                           | 149 +++++++++++++++++++++++++++--
 gcc/c/c-tree.h                             |   7 +-
 gcc/c/c-typeck.c                           |  69 +++++++++----
 gcc/testsuite/gcc.dg/param-type-mismatch.c |  55 +++++++++++
 9 files changed, 281 insertions(+), 28 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/param-type-mismatch.c

diff --git a/gcc/ast.c b/gcc/ast.c
index 07201ce..7f0217c 100644
--- a/gcc/ast.c
+++ b/gcc/ast.c
@@ -123,7 +123,12 @@ ast_node::dump (pretty_printer *pp, const char *prefix,
 
   if (m_tree)
     // FIXME: format code:
+#if 1
+    pp_printf (pp, " tree: %p", (void *)m_tree);
+#else
+    // seems to work for C++:
     pp_printf (pp, " tree: %p %qE", (void *)m_tree, m_tree);
+#endif
 
   /* Show source code.  */
   if (m_start != parent_start_loc || m_finish != parent_finish_loc)
diff --git a/gcc/ast.def b/gcc/ast.def
index be6bd25..6ca7814 100644
--- a/gcc/ast.def
+++ b/gcc/ast.def
@@ -30,16 +30,20 @@ DEF_AST_NODE (AST_DECLARATOR, "declarator")
 DEF_AST_NODE (AST_DECL_SPECIFIER_SEQ, "decl-specifier-seq")
 DEF_AST_NODE (AST_DIRECT_DECLARATOR, "direct-declarator")
 DEF_AST_NODE (AST_EXCEPTION_DECLARATION, "exception-declaration")
+DEF_AST_NODE (AST_EXPRESSION, "expression")
 DEF_AST_NODE (AST_EXPRESSION_LIST, "expression-list")
+DEF_AST_NODE (AST_EXTERNAL_DECLARATION, "external-declaration")
 DEF_AST_NODE (AST_FUNCTION_TRY_BLOCK, "function-try-block")
 DEF_AST_NODE (AST_HANDLER, "handler")
 DEF_AST_NODE (AST_HANDLER_SEQ, "handler-seq")
 DEF_AST_NODE (AST_IDENTIFIER, "identifier")
 DEF_AST_NODE (AST_ID_EXPRESSION, "id-expression")
 DEF_AST_NODE (AST_MEMBER_DECLARATION, "member-declaration")
+DEF_AST_NODE (AST_NONEMPTY_EXPR_LIST, "nonempty-expr-list")
 DEF_AST_NODE (AST_PARAMETER_DECLARATION, "parameter-declaration")
 DEF_AST_NODE (AST_PARAMETER_DECLARATION_CLAUSE, "parameter-declaration-clause")
 DEF_AST_NODE (AST_PARAMETER_DECLARATION_LIST, "parameter-declaration-list")
+DEF_AST_NODE (AST_PARAMETER_LIST, "parameter-list")
 DEF_AST_NODE (AST_POSTFIX_EXPRESSION, "postfix-expression")
 DEF_AST_NODE (AST_PRIMARY_EXPRESSION, "primary-expression")
 DEF_AST_NODE (AST_SIMPLE_DECLARATION, "simple-declaration")
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 7e359f6..6349303 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -989,7 +989,8 @@ extern tree c_add_case_label (location_t, splay_tree, tree, tree, tree, tree,
 extern tree build_function_call (location_t, tree, tree);
 
 extern tree build_function_call_vec (location_t, vec<location_t>, tree,
-				     vec<tree, va_gc> *, vec<tree, va_gc> *);
+				     vec<tree, va_gc> *, vec<tree, va_gc> *,
+				     ast_node * = NULL);
 
 extern tree resolve_overloaded_builtin (location_t, tree, vec<tree, va_gc> *);
 
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 08b5655..09be6a5 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1205,7 +1205,7 @@ C ObjC C++ ObjC++
 Where shorter, use canonicalized paths to systems headers.
 
 fcapture-ast
-C++ ObjC++ Var(flag_capture_ast)
+C ObjC C++ ObjC++ Var(flag_capture_ast)
 Capture the AST.
 
 fcheck-pointer-bounds
@@ -1363,7 +1363,7 @@ C ObjC C++ ObjC++ RejectNegative Var(flag_dump_ada_spec_slim)
 Write all declarations as Ada code for the given file only.
 
 fdump-ast
-C++ ObjC++ Var(flag_dump_ast)
+C ObjC C++ ObjC++ Var(flag_dump_ast)
 Dump the AST.
 
 felide-constructors
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 64a1107..cdbf28f 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "builtins.h"
 #include "spellcheck-tree.h"
 #include "gcc-rich-location.h"
+#include "ast.h"
 
 /* In grokdeclarator, distinguish syntactic contexts of declarators.  */
 enum decl_context
@@ -4523,6 +4524,7 @@ build_array_declarator (location_t loc,
 	}
       current_scope->had_vla_unspec = true;
     }
+  declarator->asnode = NULL;
   return declarator;
 }
 
@@ -4647,6 +4649,8 @@ start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs,
 			 deprecated_state);
   if (!decl || decl == error_mark_node)
     return NULL_TREE;
+  if (declarator->asnode)
+    declarator->asnode->set_tree (decl);
 
   if (expr)
     add_stmt (fold_convert (void_type_node, expr));
@@ -8542,6 +8546,8 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
 
   decl1 = grokdeclarator (declarator, declspecs, FUNCDEF, true, NULL,
 			  &attributes, NULL, NULL, DEPRECATED_NORMAL);
+  if (declarator->asnode)
+    declarator->asnode->set_tree (decl1);
   invoke_plugin_callbacks (PLUGIN_START_PARSE_FUNCTION, decl1);
 
   /* If the declarator is not suitable for a function definition,
@@ -9659,6 +9665,7 @@ build_attrs_declarator (tree attrs, struct c_declarator *target)
   ret->kind = cdk_attrs;
   ret->declarator = target;
   ret->u.attrs = attrs;
+  ret->asnode = NULL;
   return ret;
 }
 
@@ -9667,12 +9674,14 @@ build_attrs_declarator (tree attrs, struct c_declarator *target)
 
 struct c_declarator *
 build_function_declarator (struct c_arg_info *args,
-			   struct c_declarator *target)
+			   struct c_declarator *target,
+			   ast_node *asnode)
 {
   struct c_declarator *ret = XOBNEW (&parser_obstack, struct c_declarator);
   ret->kind = cdk_function;
   ret->declarator = target;
   ret->u.arg_info = args;
+  ret->asnode = asnode;
   return ret;
 }
 
@@ -9688,6 +9697,7 @@ build_id_declarator (tree ident)
   ret->u.id = ident;
   /* Default value - may get reset to a more precise location. */
   ret->id_loc = input_location;
+  ret->asnode = NULL;
   return ret;
 }
 
@@ -9714,6 +9724,7 @@ make_pointer_declarator (struct c_declspecs *type_quals_attrs,
   ret->kind = cdk_pointer;
   ret->declarator = itarget;
   ret->u.pointer_quals = quals;
+  ret->asnode = NULL;
   return ret;
 }
 
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 9398652..b4c36ca 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -65,6 +65,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "read-rtl-function.h"
 #include "run-rtl-passes.h"
 #include "intl.h"
+#include "ast.h"
 
 /* We need to walk over decls with incomplete struct/union/enum types
    after parsing the whole translation unit.
@@ -206,8 +207,112 @@ struct GTY(()) c_parser {
   /* Buffer to hold all the tokens from parsing the vector attribute for the
      SIMD-enabled functions (formerly known as elemental functions).  */
   vec <c_token, va_gc> *cilk_simd_fn_tokens;
+
+  location_t last_token_location;
+  ast_node * GTY((skip)) ast_root_node;
+  ast_node * GTY((skip)) ast_current_node;
+};
+
+/* FIXME.  */
+// RAII-style highlighting of the AST.
+
+/* FIXME.  */
+
+class auto_ast_node
+{
+public:
+  auto_ast_node (c_parser* parser, enum ast_kind kind);
+  ~auto_ast_node ();
+
+  void set_tree (tree node);
+
+private:
+  c_parser *m_parser;
 };
 
+/* FIXME.  */
+
+#define AUTO_AST_NODE(PARSER, KIND) \
+  auto_ast_node tmp_ast_node ((PARSER), (KIND))
+
+#define CURRENT_AST_NODE (parser->ast_current_node)
+
+#define AST_ADD_NEXT_TOKEN(PARSER) \
+  add_next_token (PARSER)
+
+#define AST_SET_TREE(TREE_NODE) \
+  tmp_ast_node.set_tree (TREE_NODE);
+
+#if 0
+static void
+add_next_token (c_parser *parser)
+{
+  if (!flag_capture_ast)
+    return;
+
+  c_token *token = c_parser_peek_token (parser);
+  ast_node *parent = parser->ast_current_node;
+  ast_node *node = new ast_node (AST_TOKEN, token->location);
+  parent->add_child (node);
+  // FIXME: get a meaningful string for the token
+  node->set_finish (token->location);
+}
+#endif
+
+/* FIXME.  */
+
+auto_ast_node::auto_ast_node (c_parser *parser, enum ast_kind kind)
+: m_parser (parser)
+{
+  if (!flag_capture_ast)
+    return;
+
+  c_token *first_token = c_parser_peek_token (parser);
+  ast_node *parent = parser->ast_current_node;
+  ast_node *node = new ast_node (kind, first_token->location);
+  parser->ast_current_node = node;
+  if (parent)
+    parent->add_child (node);
+  else
+    parser->ast_root_node = node;
+}
+
+/* FIXME.  */
+
+auto_ast_node::~auto_ast_node ()
+{
+  if (!flag_capture_ast)
+    return;
+
+  ast_node *node = m_parser->ast_current_node;
+  node->set_finish (m_parser->last_token_location);
+
+  if (0)
+    {
+      location_t start = node->get_start ();
+      location_t finish = node->get_finish ();
+      location_t range = make_location (start, start, finish);
+      inform (range, "%qs", node->get_name ());
+    }
+
+#if 0
+  if (m_expr_ptr)
+    node->set_tree (m_expr_ptr->get_value ());
+#endif
+
+  m_parser->ast_current_node = node->get_parent ();
+}
+
+void
+auto_ast_node::set_tree (tree tree_node)
+{
+  if (!flag_capture_ast)
+    return;
+
+  ast_node *node = m_parser->ast_current_node;
+  node->set_tree (tree_node);
+}
+
 /* Return a pointer to the Nth token in PARSERs tokens_buf.  */
 
 c_token *
@@ -770,6 +875,7 @@ c_parser_consume_token (c_parser *parser)
   gcc_assert (parser->tokens[0].type != CPP_EOF);
   gcc_assert (!parser->in_pragma || parser->tokens[0].type != CPP_PRAGMA_EOL);
   gcc_assert (parser->error || parser->tokens[0].type != CPP_PRAGMA);
+  parser->last_token_location = parser->tokens[0].location;
   if (parser->tokens != &parser->tokens_buf[0])
     parser->tokens++;
   else if (parser->tokens_avail == 2)
@@ -1255,7 +1361,8 @@ static struct c_expr c_parser_expression_conv (c_parser *);
 static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
 					     vec<tree, va_gc> **, location_t *,
 					     tree *, vec<location_t> *,
-					     unsigned int * = NULL);
+					     unsigned int * = NULL,
+					     ast_node ** = NULL);
 static void c_parser_oacc_declare (c_parser *);
 static void c_parser_oacc_enter_exit_data (c_parser *, bool);
 static void c_parser_oacc_update (c_parser *);
@@ -1334,6 +1441,8 @@ static void c_parser_parse_rtl_body (c_parser *parser, char *start_with_pass);
 static void
 c_parser_translation_unit (c_parser *parser)
 {
+  AUTO_AST_NODE (parser, AST_TRANSLATION_UNIT);
+
   if (c_parser_next_token_is (parser, CPP_EOF))
     {
       pedwarn (c_parser_peek_token (parser)->location, OPT_Wpedantic,
@@ -1386,6 +1495,8 @@ c_parser_translation_unit (c_parser *parser)
 static void
 c_parser_external_declaration (c_parser *parser)
 {
+  AUTO_AST_NODE (parser, AST_EXTERNAL_DECLARATION);
+
   int ext;
   switch (c_parser_peek_token (parser)->type)
     {
@@ -3391,6 +3502,8 @@ struct c_declarator *
 c_parser_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
 		     bool *seen_id)
 {
+  AUTO_AST_NODE (parser, AST_DECLARATOR);
+
   /* Parse any initial pointer part.  */
   if (c_parser_next_token_is (parser, CPP_MULT))
     {
@@ -3417,6 +3530,8 @@ static struct c_declarator *
 c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
 			    bool *seen_id)
 {
+  AUTO_AST_NODE (parser, AST_DIRECT_DECLARATOR);
+
   /* The direct declarator must start with an identifier (possibly
      omitted) or a parenthesized declarator (possibly abstract).  In
      an ordinary declarator, initial parentheses must start a
@@ -3460,6 +3575,7 @@ c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
     {
       struct c_declarator *inner
 	= build_id_declarator (c_parser_peek_token (parser)->value);
+      inner->asnode = CURRENT_AST_NODE;
       *seen_id = true;
       inner->id_loc = c_parser_peek_token (parser)->location;
       c_parser_consume_token (parser);
@@ -3496,7 +3612,8 @@ c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
 	    {
 	      inner
 		= build_function_declarator (args,
-					     build_id_declarator (NULL_TREE));
+					     build_id_declarator (NULL_TREE),
+					     CURRENT_AST_NODE);
 	      return c_parser_direct_declarator_inner (parser, *seen_id,
 						       inner);
 	    }
@@ -3644,7 +3761,7 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present,
 	return NULL;
       else
 	{
-	  inner = build_function_declarator (args, inner);
+	  inner = build_function_declarator (args, inner, CURRENT_AST_NODE);
 	  return c_parser_direct_declarator_inner (parser, id_present, inner);
 	}
     }
@@ -3659,6 +3776,8 @@ c_parser_direct_declarator_inner (c_parser *parser, bool id_present,
 static struct c_arg_info *
 c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs)
 {
+  AUTO_AST_NODE (parser, AST_PARAMETER_LIST);
+
   push_scope ();
   declare_parm_level ();
   /* If the list starts with an identifier, it is an identifier list.
@@ -3830,6 +3949,8 @@ c_parser_parms_list_declarator (c_parser *parser, tree attrs, tree expr)
 static struct c_parm *
 c_parser_parameter_declaration (c_parser *parser, tree attrs)
 {
+  AUTO_AST_NODE (parser, AST_PARAMETER_DECLARATION);
+
   struct c_declspecs *specs;
   struct c_declarator *declarator;
   tree prefix_attrs;
@@ -6385,6 +6506,8 @@ static struct c_expr
 c_parser_expr_no_commas (c_parser *parser, struct c_expr *after,
 			 tree omp_atomic_lhs)
 {
+  AUTO_AST_NODE (parser, AST_ASSIGNMENT_EXPRESSION);
+
   struct c_expr lhs, rhs, ret;
   enum tree_code code;
   location_t op_location, exp_location;
@@ -8357,6 +8480,7 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
   vec<location_t> arg_loc = vNULL;
   location_t start;
   location_t finish;
+  ast_node *args_asnode = NULL;
 
   while (true)
     {
@@ -8411,12 +8535,14 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
 	      sizeof_arg_loc[i] = UNKNOWN_LOCATION;
 	    }
 	  literal_zero_mask = 0;
+
 	  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
 	    exprlist = NULL;
 	  else
 	    exprlist = c_parser_expr_list (parser, true, false, &origtypes,
 					   sizeof_arg_loc, sizeof_arg,
-					   &arg_loc, &literal_zero_mask);
+					   &arg_loc, &literal_zero_mask,
+					   &args_asnode);
 	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
 				     "expected %<)%>");
 	  orig_expr = expr;
@@ -8440,7 +8566,7 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
 	  finish = parser->tokens_buf[0].get_finish ();
 	  expr.value
 	    = c_build_function_call_vec (expr_loc, arg_loc, expr.value,
-					 exprlist, origtypes);
+					 exprlist, origtypes, args_asnode);
 	  set_c_expr_source_range (&expr, start, finish);
 
 	  expr.original_code = ERROR_MARK;
@@ -8588,6 +8714,8 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
 static struct c_expr
 c_parser_expression (c_parser *parser)
 {
+  AUTO_AST_NODE (parser, AST_EXPRESSION);
+
   location_t tloc = c_parser_peek_token (parser)->location;
   struct c_expr expr;
   expr = c_parser_expr_no_commas (parser, NULL);
@@ -8673,8 +8801,13 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
 		    vec<tree, va_gc> **p_orig_types,
 		    location_t *sizeof_arg_loc, tree *sizeof_arg,
 		    vec<location_t> *locations,
-		    unsigned int *literal_zero_mask)
+		    unsigned int *literal_zero_mask,
+		    ast_node **out)
 {
+  AUTO_AST_NODE (parser, AST_NONEMPTY_EXPR_LIST);
+  if (out)
+    *out = CURRENT_AST_NODE;
+
   vec<tree, va_gc> *ret;
   vec<tree, va_gc> *orig_types;
   struct c_expr expr;
@@ -18179,6 +18312,10 @@ c_parse_file (void)
     using_eh_for_cleanups ();
 
   c_parser_translation_unit (the_parser);
+
+  if (flag_capture_ast && flag_dump_ast)
+    the_parser->ast_root_node->dump (stderr);
+
   the_parser = NULL;
 }
 
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 5fa32a4..e19eb35 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -447,6 +447,7 @@ struct c_declarator {
     /* For attributes.  */
     tree attrs;
   } u;
+  ast_node *asnode;
 };
 
 /* A type name.  */
@@ -572,7 +573,8 @@ extern struct c_parm *build_c_parm (struct c_declspecs *, tree,
 extern struct c_declarator *build_attrs_declarator (tree,
 						    struct c_declarator *);
 extern struct c_declarator *build_function_declarator (struct c_arg_info *,
-						       struct c_declarator *);
+						       struct c_declarator *,
+						       ast_node *);
 extern struct c_declarator *build_id_declarator (tree);
 extern struct c_declarator *make_pointer_declarator (struct c_declspecs *,
 						     struct c_declarator *);
@@ -689,7 +691,8 @@ extern tree c_build_va_arg (location_t, tree, location_t, tree);
 extern tree c_finish_transaction (location_t, tree, int);
 extern bool c_tree_equal (tree, tree);
 extern tree c_build_function_call_vec (location_t, vec<location_t>, tree,
-				       vec<tree, va_gc> *, vec<tree, va_gc> *);
+				       vec<tree, va_gc> *, vec<tree, va_gc> *,
+				       ast_node * = NULL);
 extern tree c_omp_clause_copy_ctor (tree, tree, tree);
 
 /* Set to 0 at beginning of a function definition, set to 1 if
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 00a6577..129aebb 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -50,6 +50,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gomp-constants.h"
 #include "spellcheck-tree.h"
 #include "gcc-rich-location.h"
+#include "ast.h"
 
 /* Possible cases of implicit bad conversions.  Used to select
    diagnostic messages in convert_for_assignment.  */
@@ -3001,7 +3002,8 @@ inform_declaration (tree decl)
 tree
 build_function_call_vec (location_t loc, vec<location_t> arg_loc,
 			 tree function, vec<tree, va_gc> *params,
-			 vec<tree, va_gc> *origtypes)
+			 vec<tree, va_gc> *origtypes,
+			 ast_node *args_asnode)
 {
   tree fntype, fundecl = 0;
   tree name = NULL_TREE, result;
@@ -3111,7 +3113,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, NULL);
+					    nargs, argarray, args_asnode);
 
   if (name != NULL_TREE
       && !strncmp (IDENTIFIER_POINTER (name), "__builtin_", 10))
@@ -3160,7 +3162,8 @@ build_function_call_vec (location_t loc, vec<location_t> arg_loc,
 tree
 c_build_function_call_vec (location_t loc, vec<location_t> arg_loc,
 			   tree function, vec<tree, va_gc> *params,
-			   vec<tree, va_gc> *origtypes)
+			   vec<tree, va_gc> *origtypes,
+			   ast_node *args_asnode)
 {
   /* Strip NON_LVALUE_EXPRs, etc., since we aren't using as an lvalue.  */
   STRIP_TYPE_NOPS (function);
@@ -3176,7 +3179,8 @@ c_build_function_call_vec (location_t loc, vec<location_t> arg_loc,
       if (tem)
 	return tem;
     }
-  return build_function_call_vec (loc, arg_loc, function, params, origtypes);
+  return build_function_call_vec (loc, arg_loc, function, params, origtypes,
+				  args_asnode);
 }
 
 /* Convert the argument expressions in the vector VALUES
@@ -6107,6 +6111,48 @@ maybe_warn_string_init (location_t loc, tree type, struct c_expr expr)
 		  "array initialized from parenthesized string constant");
 }
 
+/* FIXME.  */
+
+static location_t
+get_fndecl_argument_location (tree fundecl, int argnum, location_t loc)
+{
+  ast_node *node = ast_get_node_for_tree (fundecl);
+  if (!node)
+    return loc;
+
+  if (node->get_kind () != AST_DIRECT_DECLARATOR)
+    return loc;
+
+  /* We expect a parameter-list containing parameter-declaration.  */
+  node = node->get_first_child_of_kind (AST_PARAMETER_LIST);
+  if (!node)
+    return loc;
+
+  auto_vec<ast_node *> params;
+  node->get_children_of_kind (params, AST_PARAMETER_DECLARATION);
+
+  if (argnum >= (int)params.length ())
+    return loc;
+
+  ast_node *param = params[argnum];
+  return param->get_range ();
+}
+
+/* FIXME.  */
+
+static void
+inform_for_arg (tree fundecl, location_t ploc, int parmnum,
+		tree expected_type, tree actual_type)
+{
+  location_t loc = (fundecl && !DECL_IS_BUILTIN (fundecl)
+		    ? DECL_SOURCE_LOCATION (fundecl) : ploc);
+  loc = get_fndecl_argument_location (fundecl, parmnum - 1, loc);
+
+  inform (loc,
+	  "expected %qT but argument is of type %qT",
+	  expected_type, actual_type);
+}
+
 /* Convert value RHS to type TYPE as preparation for an assignment to
    an lvalue of type TYPE.  If ORIGTYPE is not NULL_TREE, it is the
    original type of RHS; this differs from TREE_TYPE (RHS) for enum
@@ -6178,10 +6224,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       {                                                                  \
       case ic_argpass:                                                   \
         if (pedwarn (PLOC, OPT, AR, parmnum, rname))			 \
-          inform ((fundecl && !DECL_IS_BUILTIN (fundecl))	         \
-		  ? DECL_SOURCE_LOCATION (fundecl) : PLOC,		 \
-                  "expected %qT but argument is of type %qT",            \
-                  type, rhstype);                                        \
+          inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);	\
         break;                                                           \
       case ic_assign:                                                    \
         pedwarn (LOCATION, OPT, AS);                                     \
@@ -6207,10 +6250,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       {                                                                  \
       case ic_argpass:                                                   \
         if (pedwarn (PLOC, OPT, AR, parmnum, rname, QUALS))		 \
-          inform ((fundecl && !DECL_IS_BUILTIN (fundecl))	         \
-		  ? DECL_SOURCE_LOCATION (fundecl) : PLOC,		 \
-                  "expected %qT but argument is of type %qT",            \
-                  type, rhstype);                                        \
+          inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);	\
         break;                                                           \
       case ic_assign:                                                    \
         pedwarn (LOCATION, OPT, AS, QUALS);				 \
@@ -6236,10 +6276,7 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
       {                                                                  \
       case ic_argpass:                                                   \
         if (warning_at (PLOC, OPT, AR, parmnum, rname, QUALS))           \
-          inform ((fundecl && !DECL_IS_BUILTIN (fundecl))                \
-                  ? DECL_SOURCE_LOCATION (fundecl) : PLOC,               \
-                  "expected %qT but argument is of type %qT",            \
-                  type, rhstype);                                        \
+          inform_for_arg (fundecl, (PLOC), parmnum, type, rhstype);      \
         break;                                                           \
       case ic_assign:                                                    \
         warning_at (LOCATION, OPT, AS, QUALS);                           \
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..487978a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/param-type-mismatch.c
@@ -0,0 +1,55 @@
+/* { dg-options "-fdiagnostics-show-caret -fcapture-ast" }  */
+
+/* decl, with argname.  */
+
+extern int callee_1 (int one, const char *two, float three); /* { dg-message "expected 'const char \\*' but argument is of type 'int'" } */
+/* { dg-begin-multiline-output "" }
+ extern int callee_1 (int one, const char *two, float three);
+                               ^~~~~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+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 "" } */
+}
+
+/* decl, without argname.  */
+
+extern int callee_2 (int, const char *, float); /* { dg-message "expected 'const char \\*' but argument is of type 'int'" } */
+/* { dg-begin-multiline-output "" }
+ extern int callee_2 (int, const char *, float);
+                           ^~~~~~~~~~~~
+   { dg-end-multiline-output "" } */
+
+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 "" } */
+}
+
+/* defn, with argname.  */
+
+static int callee_3 (int one, const char *two, float three) /* { dg-message "expected 'const char \\*' but argument is of type 'int'" } */
+/* { dg-begin-multiline-output "" }
+ static int callee_3 (int one, const char *two, float three)
+                               ^~~~~~~~~~~~~~~
+     { dg-end-multiline-output "" } */
+{
+  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 "" } */
+}
-- 
1.8.5.3

