]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
Filter: Print proper error response in multiple method dispatch
authorOndrej Zajicek <santiago@crfreenet.org>
Wed, 12 Jul 2023 18:11:34 +0000 (20:11 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Tue, 12 Sep 2023 14:33:59 +0000 (16:33 +0200)
When no matching method is found, print an error response containing
position and type of infringing argument and a set of expected types.

filter/data.c
filter/data.h
filter/f-util.c

index 581150a2c51262e80055131b3a5ba141778d0311..deca00e0a3b9032c6e20dd4803994539fd932e1d 100644 (file)
@@ -27,6 +27,7 @@
 
 static const char * const f_type_str[] = {
   [T_VOID]     = "void",
+  [T_NONE]     = "none",
 
   [T_INT]      = "int",
   [T_BOOL]     = "bool",
index e136b65f3ebfd61225a48b7cf027265ff244c5ef..6420d37337d6091409afa21cac937a3b1b90bd0e 100644 (file)
@@ -20,6 +20,8 @@ enum f_type {
 /* Nothing. Simply nothing. */
   T_VOID = 0,
 
+  T_NONE = 1,          /* Special hack to represent missing arguments */
+
 /* User visible types, which fit in int */
   T_INT = 0x10,
   T_BOOL = 0x11,
index 7ce3f9c8c9422220827a72f5f8e85137958a6e09..e980cef5667688e8d5ad1b5f34c8001be539e535 100644 (file)
@@ -45,25 +45,97 @@ f_new_where(struct f_inst *where)
 static inline int
 f_match_signature(const struct f_method *dsc, struct f_inst *args)
 {
-  uint i;
+  int i, arg_num = (int) dsc->arg_num;
 
-  for (i = 1; args && (i < dsc->arg_num); args = args->next, i++)
+  for (i = 1; args && (i < arg_num); args = args->next, i++)
     if (dsc->args_type[i] && (args->type != dsc->args_type[i]))
       return 0;
 
-  return !args && !(i < dsc->arg_num);
+  return !args && !(i < arg_num);
+}
+
+/* Variant of f_match_signature(), optimized for error reporting */
+static inline void
+f_match_signature_err(const struct f_method *dsc, struct f_inst *args, int *pos, int *want, int *got)
+{
+  int i, arg_num = (int) dsc->arg_num;
+
+  for (i = 1; args && (i < arg_num); args = args->next, i++)
+    if (dsc->args_type[i] && (args->type != dsc->args_type[i]))
+      break;
+
+  *pos = i;
+  *want = (i < arg_num) ? dsc->args_type[i] : T_NONE;
+  *got = args ? args->type : T_NONE;
 }
 
 struct f_inst *
 f_dispatch_method(struct symbol *sym, struct f_inst *obj, struct f_inst *args)
 {
-  /* Note! We should revert args */
-
+  /* Find match */
   for (const struct f_method *dsc = sym->method; dsc; dsc = dsc->next)
     if (f_match_signature(dsc, args))
       return dsc->new_inst(obj, args);
 
-  cf_error("Cannot dispatch method '%s'", sym->name);
+
+  /* No valid match - format error message */
+
+  int best_pos = -1;   /* Longest argument position with partial match */
+  int best_got = 0;    /* Received type at best partial match position */
+  int best_count = 0;  /* Number of partial matches at best position */
+  const int best_max = 8;      /* Max number of reported types */
+  int best_want[best_max];     /* Expected types at best position */
+
+  for (const struct f_method *dsc = sym->method; dsc; dsc = dsc->next)
+  {
+    int pos, want, got;
+    f_match_signature_err(dsc, args, &pos, &want, &got);
+
+    /* Ignore shorter match */
+    if (pos < best_pos)
+      continue;
+
+    /* Found longer match, reset existing results */
+    if (pos > best_pos)
+    {
+      best_pos = pos;
+      best_got = got;
+      best_count = 0;
+    }
+
+    /* Skip duplicates */
+    for (int i = 0; i < best_count; i++)
+      if (best_want[i] == want)
+       goto next;
+
+    /* Skip if we have enough types */
+    if (best_count >= best_max)
+      continue;
+
+    /* Add new expected type */
+    best_want[best_count] = want;
+    best_count++;
+  next:;
+  }
+
+  /* There is at least one method */
+  ASSERT(best_pos >= 0 && best_count > 0);
+
+  /* TODO: Here fix best_pos */
+
+  if (!best_got)
+    cf_error("Cannot infer type of argument %d of '%s', please assign it to a variable", best_pos, sym->name);
+
+  /* Format list of expected types */
+  buffer tbuf;
+  STACK_BUFFER_INIT(tbuf, 128);
+  for (int i = 0; i < best_count; i++)
+    buffer_print(&tbuf, " / %s", best_want[i] ? f_type_name(best_want[i]) : "any");
+  char *types = tbuf.start + 3;
+  char *dots = (best_count >= best_max) || (tbuf.pos == tbuf.end) ? " / ..." : "";
+
+  cf_error("Argument %d of '%s' expected %s%s, got %s",
+          best_pos, sym->name, types, dots, f_type_name(best_got));
 }
 
 struct f_inst *
@@ -87,7 +159,7 @@ f_for_cycle(struct symbol *var, struct f_inst *term, struct f_inst *block)
 
   /* Static type check */
   if (term->type == T_VOID)
-    cf_error("Couldn't infer the type of FOR expression, please assign it to a variable.");
+    cf_error("Cannot infer type of FOR expression, please assign it to a variable");
 
   enum f_type el_type = f_type_element_type(term->type);
   struct sym_scope *scope = el_type ? f_type_method_scope(term->type) : NULL;