From a1703bf92fba1645e7168ef9a2488fbf33ea1cf0 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 22 Jun 2018 10:08:14 -0400
Subject: [PATCH 19/19] FIXME: WIP on argument count mismatch (PR c/70618)

---
 gcc/c/c-typeck.c                           | 133 +++++++++++
 gcc/diagnostic-core.h                      |   6 +
 gcc/diagnostic-show-locus.c                |   5 +-
 gcc/diagnostic.c                           |  28 +++
 gcc/edit-distance.h                        | 365 +++++++++++++++++++++++++++++
 gcc/spellcheck.c                           |  99 +-------
 gcc/testsuite/gcc.dg/argument-mismatches.c |  66 ++++++
 7 files changed, 605 insertions(+), 97 deletions(-)
 create mode 100644 gcc/edit-distance.h
 create mode 100644 gcc/testsuite/gcc.dg/argument-mismatches.c

diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 54c7967..b797dfd 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -52,6 +52,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "edit-distance.h"
 
 /* Possible cases of implicit bad conversions.  Used to select
    diagnostic messages in convert_for_assignment.  */
@@ -83,6 +84,9 @@ static int found_missing_braces;
 static int require_constant_value;
 static int require_constant_elements;
 
+// FIXME: where, relative to other ones?
+static location_t get_fndecl_argument_location (tree fndecl, int argnum);
+
 static bool null_pointer_constant_p (const_tree);
 static tree qualify_type (tree, tree);
 static int tagged_types_tu_compatible_p (const_tree, const_tree, bool *,
@@ -3185,6 +3189,131 @@ c_build_function_call_vec (location_t loc, vec<location_t> arg_loc,
   return build_function_call_vec (loc, arg_loc, function, params, origtypes);
 }
 
+
+/* FIXME: return true if we diagnosed an error.  */
+
+static bool
+maybe_diagnose_mismatching_param_count (vec<location_t> arg_loc, tree typelist,
+					vec<tree, va_gc> *values, tree fndecl,
+					location_t loc)
+{
+  auto_vec<tree> expected_types;
+  for (tree type_node = typelist;
+       type_node && TREE_VALUE (type_node) != void_type_node;
+       type_node = TREE_CHAIN (type_node))
+    expected_types.safe_push (TREE_VALUE (type_node));
+  // or could just get the count up-front?
+  // FIXME: is this getting it wrong?  (one too many?)
+
+  // TODO: handle values being NULL
+  const unsigned actual_length = values ? values->length () : 0;
+
+  if (expected_types.length () == actual_length)
+    return false;
+
+  // TODO: handle various other cases e.g. transposition?
+
+  /* Compare the arguments types that were expected to the argument types
+     that we have.  */
+
+  auto_vec<tree> actual_types (actual_length);
+  for (unsigned i = 0; i < actual_length; i++)
+    actual_types.safe_push (TREE_TYPE ((*values)[i]));
+
+  edit_script edits;
+  get_edit_script<tree> (expected_types.address (),
+			 expected_types.length (),
+			 actual_types.address (),
+			 actual_types.length (),
+			 &edits);
+
+  auto_diagnostic_group d;
+
+  /* Issue an error describing the mismatching argument count.
+     Attempt to put the error location at first place the user
+     needs to touch.  */
+
+  if (actual_length > expected_types.length ())
+    {
+      /* Too many arguments.  */
+
+      if (edits.m_insertions.length () > 0)
+	{
+	  auto_vec<int, 16> arg_positions;
+	  int dst_idx = edits.m_insertions[0].dst_idx;
+	  arg_positions.safe_push (dst_idx + 1);
+
+	  gcc_rich_location richloc (arg_loc[dst_idx]);
+	  for (unsigned i = 1; i < edits.m_insertions.length (); i++)
+	    {
+	      dst_idx = edits.m_insertions[i].dst_idx;
+	      arg_positions.safe_push (dst_idx + 1);
+	      richloc.add_range (arg_loc[dst_idx], NULL);
+	    }
+	  error_n (&richloc, edits.m_insertions.length (),
+		   "too many arguments to function call"
+		   " (expected %i, have %i; surplus argument at position %Z)",
+		   " (expected %i, have %i; surplus arguments at positions %Z)",
+		   expected_types.length (), actual_length,
+		   arg_positions.address (), arg_positions.length ());
+	}
+      else
+	error_at (loc,
+		  "too many arguments to function call (expected %i, have %i)",
+		  expected_types.length (), actual_length);
+    }
+  else
+    {
+      /* Too few arguments.  */
+
+      /* Use the location of where the first argument needs adding.  */
+      if (edits.m_deletions.length () > 0)
+	{
+	  unsigned src_idx = edits.m_deletions[0].src_idx;
+#if 0
+	  fprintf (stderr, "\nedits.m_deletions[0]: src_idx: %i dst_idx: %i\n",
+		   edits.m_deletions[0].src_idx, edits.m_deletions[0].dst_idx);
+#endif
+	  if (src_idx >= arg_loc.length ())
+	    src_idx = arg_loc.length () - 1;
+	  loc = arg_loc[src_idx];
+	}
+      error_at (loc,
+		"too few arguments to function call (expected %i, have %i)",
+		expected_types.length (), actual_length);
+    }
+
+  // FIXME: do we want to impose a threshold on the edit distance to report?
+
+  /* Issue a note describing the function decl, highlighting any missing
+     arguments.  */
+  if (edits.m_deletions.length () > 0)
+    {
+      auto_vec<int, 16> arg_positions;
+      int src_idx = edits.m_deletions[0].src_idx;
+      arg_positions.safe_push (src_idx + 1);
+      location_t param_loc = get_fndecl_argument_location (fndecl, src_idx);
+      // FIXME: what about builtins etc; compare with inform_declaration
+      gcc_rich_location richloc (param_loc);
+      for (unsigned i = 1; i < edits.m_deletions.length (); i++)
+	{
+	  src_idx = edits.m_deletions[i].src_idx;
+	  param_loc = get_fndecl_argument_location (fndecl, src_idx);
+	  richloc.add_range (param_loc, NULL);
+	  arg_positions.safe_push (src_idx + 1);
+	}
+      inform_n (&richloc, edits.m_deletions.length (),
+		"declared here; missing argument %Z",
+		"declared here; missing arguments %Z",
+		arg_positions.address (), arg_positions.length ());
+    }
+  else
+    inform_declaration (fndecl);
+
+  return true;
+}
+
+
 /* Convert the argument expressions in the vector VALUES
    to the types in the list TYPELIST.
 
@@ -3260,6 +3389,10 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree typelist,
 	}
     }
 
+  if (maybe_diagnose_mismatching_param_count (arg_loc, typelist, values,
+					      function, loc))
+    return -1; // FIXME
+
   /* Scan the given expressions and types, producing individual
      converted arguments.  */
 
diff --git a/gcc/diagnostic-core.h b/gcc/diagnostic-core.h
index e4ebe00..79d3898 100644
--- a/gcc/diagnostic-core.h
+++ b/gcc/diagnostic-core.h
@@ -82,6 +82,9 @@ extern void error (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern void error_n (location_t, unsigned HOST_WIDE_INT, const char *,
 		     const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
+extern void error_n (rich_location *, unsigned HOST_WIDE_INT, const char *,
+		     const char *, ...)
+    ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
 extern void error_at (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
 extern void error_at (rich_location *, const char *, ...)
   ATTRIBUTE_GCC_DIAG(2,3);
@@ -101,6 +104,9 @@ extern void inform (rich_location *, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
 extern void inform_n (location_t, unsigned HOST_WIDE_INT, const char *,
 		      const char *, ...)
     ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
+extern void inform_n (rich_location *, unsigned HOST_WIDE_INT, const char *,
+		      const char *, ...)
+    ATTRIBUTE_GCC_DIAG(3,5) ATTRIBUTE_GCC_DIAG(4,5);
 extern void verbatim (const char *, ...) ATTRIBUTE_GCC_DIAG(1,2);
 extern bool emit_diagnostic (diagnostic_t, location_t, int,
 			     const char *, ...) ATTRIBUTE_GCC_DIAG(4,5);
diff --git a/gcc/diagnostic-show-locus.c b/gcc/diagnostic-show-locus.c
index 9c0671e..94a372b 100644
--- a/gcc/diagnostic-show-locus.c
+++ b/gcc/diagnostic-show-locus.c
@@ -2261,9 +2261,10 @@ diagnostic_show_locus (diagnostic_context * context,
     return;
 
   /* Don't print the same source location twice in a row, unless we have
-     fix-it hints.  */
+     fix-it hints, or multiple locations to show.  */
   if (loc == context->last_location
-      && richloc->get_num_fixit_hints () == 0)
+      && richloc->get_num_fixit_hints () == 0
+      && richloc->get_num_locations () == 1)
     {
       /* Bail out, retaining the value of context->last_margin.  */
       context->last_margin = saved_last_margin;
diff --git a/gcc/diagnostic.c b/gcc/diagnostic.c
index 3bebf68..0b22860 100644
--- a/gcc/diagnostic.c
+++ b/gcc/diagnostic.c
@@ -1284,6 +1284,20 @@ inform_n (location_t location, unsigned HOST_WIDE_INT n,
   va_end (ap);
 }
 
+/* Same as "inform_n" above, but at RICHLOC.  */
+
+void
+inform_n (rich_location *richloc, unsigned HOST_WIDE_INT n,
+	  const char *singular_gmsgid, const char *plural_gmsgid, ...)
+{
+  va_list ap;
+  va_start (ap, plural_gmsgid);
+  auto_diagnostic_group d;
+  diagnostic_n_impl (richloc, -1, n, singular_gmsgid, plural_gmsgid,
+		     &ap, DK_NOTE);
+  va_end (ap);
+}
+
 /* A warning at INPUT_LOCATION.  Use this for code which is correct according
    to the relevant language specification but is likely to be buggy anyway.
    Returns true if the warning was printed, false if it was inhibited.  */
@@ -1469,6 +1483,20 @@ error_n (location_t location, unsigned HOST_WIDE_INT n,
   va_end (ap);
 }
 
+/* Same as above, but use RICH_LOC.  */
+
+void
+error_n (rich_location *richloc, unsigned HOST_WIDE_INT n,
+	 const char *singular_gmsgid, const char *plural_gmsgid, ...)
+{
+  auto_diagnostic_group d;
+  va_list ap;
+  va_start (ap, plural_gmsgid);
+  diagnostic_n_impl (richloc, -1, n, singular_gmsgid, plural_gmsgid,
+		     &ap, DK_ERROR);
+  va_end (ap);
+}
+
 /* Same as above, but use location LOC instead of input_location.  */
 void
 error_at (location_t loc, const char *gmsgid, ...)
diff --git a/gcc/edit-distance.h b/gcc/edit-distance.h
new file mode 100644
index 0000000..118af7c
--- /dev/null
+++ b/gcc/edit-distance.h
@@ -0,0 +1,365 @@
+/* FIXME.
+   Copyright (C) 2015-2018 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_EDIT_DISTANCE_H
+#define GCC_EDIT_DISTANCE_H
+
+/* Get the edit distance between the two sequences: the minimal
+   number of edits that are needed to change one sequence into another,
+   where edits can be one-character insertions, removals, or substitutions,
+   or transpositions of two adjacent characters (counting as one "edit").
+
+   This implementation uses the Wagner-Fischer algorithm for the
+   Damerau-Levenshtein distance; specifically, the "optimal string alignment
+   distance" or "restricted edit distance" variant.  */
+
+template <typename Element_t>
+edit_distance_t
+get_edit_distance (const Element_t *s, int len_s,
+		   const Element_t *t, int len_t,
+		   const bool debug = false)
+{
+
+#if 0
+  if (debug)
+    {
+      printf ("s: \"%s\" (len_s=%i)\n", s, len_s);
+      printf ("t: \"%s\" (len_t=%i)\n", t, len_t);
+    }
+#endif
+
+  if (len_s == 0)
+    return len_t;
+  if (len_t == 0)
+    return len_s;
+
+  /* We effectively build a matrix where each (i, j) contains the
+     distance between the prefix strings s[0:j] and t[0:i].
+     Rather than actually build an (len_t + 1) * (len_s + 1) matrix,
+     we simply keep track of the last two rows, v_one_ago and v_two_ago,
+     and a new row, v_next, which avoids an (len_t + 1) * (len_s + 1)
+     allocation and memory accesses in favor of three (len_s + 1)
+     allocations.  These could potentially be
+     statically-allocated if we impose a maximum length on the
+     strings of interest.  */
+  edit_distance_t *v_two_ago = new edit_distance_t[len_s + 1];
+  edit_distance_t *v_one_ago = new edit_distance_t[len_s + 1];
+  edit_distance_t *v_next = new edit_distance_t[len_s + 1];
+
+  /* The first row is for the case of an empty target sequence, which
+     we can reach by deleting every element in the source sequence.  */
+  for (int i = 0; i < len_s + 1; i++)
+    v_one_ago[i] = i;
+
+  /* Build successive rows.  */
+  for (int i = 0; i < len_t; i++)
+    {
+      if (debug)
+	{
+	  printf ("i:%i v_one_ago = ", i);
+	  for (int j = 0; j < len_s + 1; j++)
+	    printf ("%i ", v_one_ago[j]);
+	  printf ("\n");
+	}
+
+      /* The initial column is for the case of an empty source sequence; we
+	 can reach prefixes of the target sequence of length i
+	 by inserting i elements.  */
+      v_next[0] = i + 1;
+
+      /* Build the rest of the row by considering neighbors to
+	 the north, west and northwest.  */
+      for (int j = 0; j < len_s; j++)
+	{
+	  edit_distance_t cost = (s[j] == t[i] ? 0 : 1);
+	  edit_distance_t deletion     = v_next[j] + 1;
+	  edit_distance_t insertion    = v_one_ago[j + 1] + 1;
+	  edit_distance_t substitution = v_one_ago[j] + cost;
+	  edit_distance_t cheapest = MIN (deletion, insertion);
+	  cheapest = MIN (cheapest, substitution);
+	  if (i > 0 && j > 0 && s[j] == t[i - 1] && s[j - 1] == t[i])
+	    {
+	      edit_distance_t transposition = v_two_ago[j - 1] + 1;
+	      cheapest = MIN (cheapest, transposition);
+	    }
+	  v_next[j + 1] = cheapest;
+	}
+
+      /* Prepare to move on to next row.  */
+      for (int j = 0; j < len_s + 1; j++)
+	{
+	  v_two_ago[j] = v_one_ago[j];
+	  v_one_ago[j] = v_next[j];
+	}
+    }
+
+  if (debug)
+    {
+      printf ("final v_next = ");
+      for (int j = 0; j < len_s + 1; j++)
+	printf ("%i ", v_next[j]);
+      printf ("\n");
+    }
+
+  edit_distance_t result = v_next[len_s];
+  delete[] v_two_ago;
+  delete[] v_one_ago;
+  delete[] v_next;
+  return result;
+}
+
+/* FIXME.
+   Alternative version which retains the matrix.  */
+
+class matrix
+{
+ public:
+  matrix (int w, int h)
+  : m_w (w), m_h(h),
+    m_values (new edit_distance_t[w * h])
+  {}
+
+  ~matrix ()
+  {
+    delete[] m_values;
+  }
+
+  edit_distance_t& at (int x, int y)
+  {
+    gcc_assert (x >= 0);
+    gcc_assert (x < m_w);
+    gcc_assert (y >= 0);
+    gcc_assert (y < m_h);
+    return m_values[(y * m_w) + x];
+  }
+
+  void dump (FILE *outfile)
+  {
+    for (int y = 0; y < m_h; y++)
+      {
+	for (int x = 0; x < m_w; x++)
+	  fprintf (outfile, "%2i ", at (x, y));
+	fprintf (outfile, "\n");
+      }
+    fprintf (outfile, "\n");
+  }
+
+ private:
+  int m_w;
+  int m_h;
+  edit_distance_t *m_values;
+};
+
+struct edit_script
+{
+  struct index_pair
+  {
+    unsigned src_idx;
+    unsigned dst_idx;
+  };
+
+  void add_substitution (unsigned src_idx, unsigned dst_idx)
+  {
+    index_pair p = {src_idx, dst_idx};
+    m_substitutions.safe_push (p);
+  }
+  void add_insertion (unsigned src_idx, unsigned dst_idx)
+  {
+    index_pair p = {src_idx, dst_idx};
+    m_insertions.safe_push (p);
+  }
+  void add_deletion (unsigned src_idx, unsigned dst_idx)
+  {
+    index_pair p = {src_idx, dst_idx};
+    m_deletions.safe_push (p);
+  }
+
+  auto_vec<index_pair> m_insertions;
+  auto_vec<index_pair> m_deletions;
+  auto_vec<index_pair> m_substitutions;
+};
+
+/* FIXME.  */
+
+template <typename Element_t>
+edit_distance_t
+get_edit_script (const Element_t *s, int len_s,
+		 const Element_t *t, int len_t,
+		 edit_script *out_script,
+		 const bool debug = false)
+{
+
+#if 0
+  if (debug)
+    {
+      printf ("s: \"%s\" (len_s=%i)\n", s, len_s);
+      printf ("t: \"%s\" (len_t=%i)\n", t, len_t);
+    }
+#endif
+
+  if (len_s == 0)
+    return len_t;
+  if (len_t == 0)
+    return len_s;
+
+  /* We build a matrix where each (i, j) contains the
+     distance between the prefix strings s[0:j] and t[0:i].  */
+  matrix v (len_s + 1, len_t + 1);
+
+  /* The first row is for the case of an empty target sequence, which
+     we can reach by deleting every element in the source sequence.  */
+  for (int x = 0; x < len_s + 1; x++)
+    v.at(x, 0) = x;
+
+  /* Build successive rows.  */
+  for (int y = 0; y < len_t; y++)
+    {
+#if 0
+      if (debug)
+	{
+	  printf ("i:%i v_one_ago = ", i);
+	  for (int j = 0; j < len_s + 1; j++)
+	    printf ("%i ", v_one_ago[j]);
+	  printf ("\n");
+	}
+#endif
+
+      /* The initial column is for the case of an empty source sequence; we
+	 can reach prefixes of the target sequence of length y
+	 by inserting y elements.  */
+      v.at(0, y + 1) = y + 1;
+
+      /* Build the rest of the row by considering neighbors to
+	 the north, west and northwest.  */
+      for (int x = 0; x < len_s; x++)
+	{
+	  edit_distance_t cost = (s[x] == t[y] ? 0 : 1);
+	  edit_distance_t deletion     = v.at (x, y + 1) + 1;
+	  edit_distance_t insertion    = v.at (x + 1, y) + 1;
+	  edit_distance_t substitution = v.at (x, y) + cost;
+	  edit_distance_t cheapest = MIN (deletion, insertion);
+	  cheapest = MIN (cheapest, substitution);
+#if 0
+	  if (x > 0 && y > 0 && s[x] == t[y - 1] && s[x - 1] == t[y])
+	    {
+	      edit_distance_t transposition = v.at (x - 1, y - 1) + 1;
+	      cheapest = MIN (cheapest, transposition);
+	    }
+#endif
+	  v.at (x + 1, y + 1) = cheapest;
+	}
+    }
+
+  if (debug)
+    v.dump (stderr);
+
+  /* Figure out an edit script.  */
+  if (out_script)
+    {
+      /* Start at bottom-right corner; follow cheapest route
+	 back to top-left. */
+      unsigned x = len_s;
+      unsigned y = len_t;
+      while (x > 0 && y > 0)
+	{
+	  if (s[x - 1] == t[y - 1])
+	    {
+#if 0
+	      if (debug)
+		fprintf (stderr, "equality at (%i, %i): %c == %c\n", x, y,
+			 s[x - 1], t[y - 1]);
+#endif
+	      x--;
+	      y--;
+	      continue;
+	    }
+	  edit_distance_t insertion = v.at (x, y - 1);
+	  edit_distance_t deletion = v.at (x - 1, y);
+	  edit_distance_t substitution = v.at (x - 1, y - 1);
+	  // FIXME: transposition?
+	  if (substitution < deletion && substitution < insertion)
+	    {
+#if 0
+	      if (debug)
+		fprintf (stderr, "substitution at (%i, %i): %c -> %c\n", x, y,
+			 s[x - 1], t[y - 1]);
+#endif
+	      out_script->add_substitution (x - 1, y - 1);
+	      x--;
+	      y--;
+	      continue;
+	    }
+	  if (deletion <= insertion)
+	    {
+#if 0
+	      if (debug)
+		fprintf (stderr, "deletion at (%i, %i): %c\n", x, y,
+			 s[x - 1]);
+#endif
+	      out_script->add_deletion (x - 1, y);
+	      x--;
+	      continue;
+	    }
+	  else
+	    {
+#if 0
+	      if (debug)
+		fprintf (stderr, "insertion at (%i, %i): %c\n", x, y,
+			 t[y - 1]);
+#endif
+	      out_script->add_insertion (x, y - 1);
+	      y--;
+	      continue;
+	    }
+	}
+      while (x > 0)
+	{
+#if 0
+	  if (debug)
+	    fprintf (stderr, "deletion at (%i, %i): %c\n", x, y,
+		     s[x - 1]);
+#endif
+	  out_script->add_deletion (x - 1, y);
+	  x--;
+	}
+      while (y > 0)
+	{
+#if 0
+	  if (debug)
+	    fprintf (stderr, "insertion at (%i, %i): %c\n", x, y,
+		     t[y - 1]);
+#endif
+	  out_script->add_insertion (x, y - 1);
+	  y--;
+	}
+      if (debug)
+	printf ("\n");
+
+      /* Now reverse the events, to get the route from top-left to
+	 bottom-right.  */
+      out_script->m_insertions.reverse ();
+      out_script->m_deletions.reverse ();
+      out_script->m_substitutions.reverse ();
+  }
+
+  edit_distance_t result = v.at(len_s, len_t);
+  return result;
+}
+
+#endif  /* GCC_EDIT_DISTANCE_H  */
diff --git a/gcc/spellcheck.c b/gcc/spellcheck.c
index e2a8b92..ddfd55c 100644
--- a/gcc/spellcheck.c
+++ b/gcc/spellcheck.c
@@ -23,107 +23,16 @@ along with GCC; see the file COPYING3.  If not see
 #include "tm.h"
 #include "tree.h"
 #include "spellcheck.h"
+#include "edit-distance.h"
 #include "selftest.h"
 
-/* Get the edit distance between the two strings: the minimal
-   number of edits that are needed to change one string into another,
-   where edits can be one-character insertions, removals, or substitutions,
-   or transpositions of two adjacent characters (counting as one "edit").
+/* FIXME.  */
 
-   This implementation uses the Wagner-Fischer algorithm for the
-   Damerau-Levenshtein distance; specifically, the "optimal string alignment
-   distance" or "restricted edit distance" variant.  */
-
-edit_distance_t
+extern edit_distance_t
 get_edit_distance (const char *s, int len_s,
 		   const char *t, int len_t)
 {
-  const bool debug = false;
-
-  if (debug)
-    {
-      printf ("s: \"%s\" (len_s=%i)\n", s, len_s);
-      printf ("t: \"%s\" (len_t=%i)\n", t, len_t);
-    }
-
-  if (len_s == 0)
-    return len_t;
-  if (len_t == 0)
-    return len_s;
-
-  /* We effectively build a matrix where each (i, j) contains the
-     distance between the prefix strings s[0:j] and t[0:i].
-     Rather than actually build an (len_t + 1) * (len_s + 1) matrix,
-     we simply keep track of the last two rows, v_one_ago and v_two_ago,
-     and a new row, v_next, which avoids an (len_t + 1) * (len_s + 1)
-     allocation and memory accesses in favor of three (len_s + 1)
-     allocations.  These could potentially be
-     statically-allocated if we impose a maximum length on the
-     strings of interest.  */
-  edit_distance_t *v_two_ago = new edit_distance_t[len_s + 1];
-  edit_distance_t *v_one_ago = new edit_distance_t[len_s + 1];
-  edit_distance_t *v_next = new edit_distance_t[len_s + 1];
-
-  /* The first row is for the case of an empty target string, which
-     we can reach by deleting every character in the source string.  */
-  for (int i = 0; i < len_s + 1; i++)
-    v_one_ago[i] = i;
-
-  /* Build successive rows.  */
-  for (int i = 0; i < len_t; i++)
-    {
-      if (debug)
-	{
-	  printf ("i:%i v_one_ago = ", i);
-	  for (int j = 0; j < len_s + 1; j++)
-	    printf ("%i ", v_one_ago[j]);
-	  printf ("\n");
-	}
-
-      /* The initial column is for the case of an empty source string; we
-	 can reach prefixes of the target string of length i
-	 by inserting i characters.  */
-      v_next[0] = i + 1;
-
-      /* Build the rest of the row by considering neighbors to
-	 the north, west and northwest.  */
-      for (int j = 0; j < len_s; j++)
-	{
-	  edit_distance_t cost = (s[j] == t[i] ? 0 : 1);
-	  edit_distance_t deletion     = v_next[j] + 1;
-	  edit_distance_t insertion    = v_one_ago[j + 1] + 1;
-	  edit_distance_t substitution = v_one_ago[j] + cost;
-	  edit_distance_t cheapest = MIN (deletion, insertion);
-	  cheapest = MIN (cheapest, substitution);
-	  if (i > 0 && j > 0 && s[j] == t[i - 1] && s[j - 1] == t[i])
-	    {
-	      edit_distance_t transposition = v_two_ago[j - 1] + 1;
-	      cheapest = MIN (cheapest, transposition);
-	    }
-	  v_next[j + 1] = cheapest;
-	}
-
-      /* Prepare to move on to next row.  */
-      for (int j = 0; j < len_s + 1; j++)
-	{
-	  v_two_ago[j] = v_one_ago[j];
-	  v_one_ago[j] = v_next[j];
-	}
-    }
-
-  if (debug)
-    {
-      printf ("final v_next = ");
-      for (int j = 0; j < len_s + 1; j++)
-	printf ("%i ", v_next[j]);
-      printf ("\n");
-    }
-
-  edit_distance_t result = v_next[len_s];
-  delete[] v_two_ago;
-  delete[] v_one_ago;
-  delete[] v_next;
-  return result;
+  return get_edit_distance<char> (s, len_s, t, len_t);
 }
 
 /* Get the edit distance between two nil-terminated strings.  */
diff --git a/gcc/testsuite/gcc.dg/argument-mismatches.c b/gcc/testsuite/gcc.dg/argument-mismatches.c
new file mode 100644
index 0000000..c45cc26
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/argument-mismatches.c
@@ -0,0 +1,66 @@
+/* PR 70618.  */
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+int x;
+float y;
+double z;
+short k; 
+
+extern void callee_1 (int *xp, float *yp, double *zp);
+
+void test_1(void)
+{
+  /* forgot x */
+  callee_1 (&y, &z);
+}
+
+extern void foo (int *xp, float *yp, double *zp);
+
+void test_2 (void)
+{
+  foo(&x, &z);    /* forgot y */
+  foo(&x, &y);    /* forgot z */
+  foo(&x);        /* forgot y and z */
+  foo(&y);        /* forgot x and z */
+  foo(&z);        /* forgot x and y */
+
+  foo(&x, &y, &z, &x);    /* x too many at end */
+  foo(&x, &x, &y, &z);    /* x too many at start */
+  foo(&x, &y, &y, &z);    /* y too much in the middle */
+  foo(&x, &y, &k, &z);    /* different y in middle */
+  foo(&k, &x, &y, &z);    /* different x at start */
+  foo(&x, &y, &z, &k);    /* different x at end */
+
+  foo(&x, &y,
+      &k, &z);
+
+  foo(&x, &z, &y); /* transposed y and z */
+}
+
+extern void bar (int *xp, float *yp, double *zp, ...);
+
+
+void test_3 (void)
+{
+  bar(&y, &z);    /* forgot x */
+  bar(&x, &z);    /* forgot y */
+  bar(&x, &y);    /* forgot z */
+  bar(&x);        /* forgot y and z */
+  bar(&y);        /* forgot x and z */
+  bar(&z);        /* forgot x and y */
+
+  bar(&x, &y, &z, &x);    /* x too many at end */
+  bar(&x, &x, &y, &z);    /* x too many at start */
+  bar(&x, &y, &y, &z);    /* y too much in the middle */
+  bar(&x, &y, &k, &z);    /* different y in middle */
+  bar(&k, &x, &y, &z);    /* different x at start */
+  bar(&x, &y, &z, &k);    /* different x at end */
+
+  bar(&x, &y,
+      &k, &z);
+
+  bar(&x, &z, &y); /* transposed y and z */
+// TODO: variadic stuff
+
+}
+
-- 
1.8.5.3

