]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile/internal/types2: report types for mismatched call and return statements
authorRobert Griesemer <gri@golang.org>
Wed, 17 Nov 2021 23:23:12 +0000 (15:23 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 24 Nov 2021 20:57:46 +0000 (20:57 +0000)
Thanks to emmanuel@orijtech.com who wrote the initial version of
this change (CL 354490).

This change is following CL 354490 in idea but also contains various
simplifications, slightly improved printing of signature/type patterns,
adjustments for types2, and some fine-tuning of error positions.

Also adjusted several ERROR regexp patterns.

Fixes #48834.
Fixes #48835.

Change-Id: I31cf20c81753b1dc84836dbe83a39030ceb9db23
Reviewed-on: https://go-review.googlesource.com/c/go/+/364874
Trust: Robert Griesemer <gri@golang.org>
Reviewed-by: Robert Findley <rfindley@google.com>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
15 files changed:
src/cmd/compile/internal/types2/assignments.go
src/cmd/compile/internal/types2/call.go
src/cmd/compile/internal/types2/decl.go
src/cmd/compile/internal/types2/stmt.go
src/cmd/compile/internal/types2/testdata/check/builtins.src
src/cmd/compile/internal/types2/testdata/check/errors.src
src/cmd/compile/internal/types2/testdata/check/expr3.src
src/cmd/compile/internal/types2/testdata/check/stmt0.src
src/cmd/compile/internal/types2/testdata/check/typeparams.go2
src/cmd/compile/internal/types2/testdata/check/vardecl.src
test/fixedbugs/bug326.go
test/fixedbugs/issue4215.go
test/fixedbugs/issue46957.go
test/fixedbugs/issue48834.go [new file with mode: 0644]
test/fixedbugs/issue48835.go [new file with mode: 0644]

index ac4f7b88a49821212d3115cf7bae8e79a6a20d98..0a85d8eb3970de621b8982e8483e851ddc4dcf01 100644 (file)
@@ -9,6 +9,7 @@ package types2
 import (
        "cmd/compile/internal/syntax"
        "fmt"
+       "strings"
 )
 
 // assignment reports whether x can be assigned to a variable of type T,
@@ -241,6 +242,58 @@ func (check *Checker) assignVar(lhs syntax.Expr, x *operand) Type {
        return x.typ
 }
 
+// operandTypes returns the list of types for the given operands.
+func operandTypes(list []*operand) (res []Type) {
+       for _, x := range list {
+               res = append(res, x.typ)
+       }
+       return res
+}
+
+// varTypes returns the list of types for the given variables.
+func varTypes(list []*Var) (res []Type) {
+       for _, x := range list {
+               res = append(res, x.typ)
+       }
+       return res
+}
+
+// typesSummary returns a string of the form "(t1, t2, ...)" where the
+// ti's are user-friendly string representations for the given types.
+// If variadic is set and the last type is a slice, its string is of
+// the form "...E" where E is the slice's element type.
+func (check *Checker) typesSummary(list []Type, variadic bool) string {
+       var res []string
+       for i, t := range list {
+               var s string
+               switch {
+               case t == nil:
+                       fallthrough // should not happend but be cautious
+               case t == Typ[Invalid]:
+                       s = "<T>"
+               case isUntyped(t):
+                       if isNumeric(t) {
+                               // Do not imply a specific type requirement:
+                               // "have number, want float64" is better than
+                               // "have untyped int, want float64" or
+                               // "have int, want float64".
+                               s = "number"
+                       } else {
+                               // If we don't have a number, omit the "untyped" qualifier
+                               // for compactness.
+                               s = strings.Replace(t.(*Basic).name, "untyped ", "", -1)
+                       }
+               case variadic && i == len(list)-1:
+                       s = check.sprintf("...%s", t.(*Slice).elem)
+               }
+               if s == "" {
+                       s = check.sprintf("%s", t)
+               }
+               res = append(res, s)
+       }
+       return "(" + strings.Join(res, ", ") + ")"
+}
+
 func (check *Checker) assignError(rhs []syntax.Expr, nvars, nvals int) {
        measure := func(x int, unit string) string {
                s := fmt.Sprintf("%d %s", x, unit)
@@ -263,10 +316,10 @@ func (check *Checker) assignError(rhs []syntax.Expr, nvars, nvals int) {
        check.errorf(rhs0, "assignment mismatch: %s but %s", vars, vals)
 }
 
-// If returnPos is valid, initVars is called to type-check the assignment of
-// return expressions, and returnPos is the position of the return statement.
-func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syntax.Pos) {
-       rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2 && !returnPos.IsKnown())
+// If returnStmt != nil, initVars is called to type-check the assignment
+// of return expressions, and returnStmt is the the return statement.
+func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnStmt syntax.Stmt) {
+       rhs, commaOk := check.exprList(orig_rhs, len(lhs) == 2 && returnStmt == nil)
 
        if len(lhs) != len(rhs) {
                // invalidate lhs
@@ -282,8 +335,20 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syn
                                return
                        }
                }
-               if returnPos.IsKnown() {
-                       check.errorf(returnPos, "wrong number of return values (want %d, got %d)", len(lhs), len(rhs))
+               if returnStmt != nil {
+                       var at poser = returnStmt
+                       qualifier := "not enough"
+                       if len(rhs) > len(lhs) {
+                               at = rhs[len(lhs)].expr // report at first extra value
+                               qualifier = "too many"
+                       } else if len(rhs) > 0 {
+                               at = rhs[len(rhs)-1].expr // report at last value
+                       }
+                       var err error_
+                       err.errorf(at, "%s return values", qualifier)
+                       err.errorf(nopos, "have %s", check.typesSummary(operandTypes(rhs), false))
+                       err.errorf(nopos, "want %s", check.typesSummary(varTypes(lhs), false))
+                       check.report(&err)
                        return
                }
                if check.conf.CompilerErrorMessages {
@@ -295,7 +360,7 @@ func (check *Checker) initVars(lhs []*Var, orig_rhs []syntax.Expr, returnPos syn
        }
 
        context := "assignment"
-       if returnPos.IsKnown() {
+       if returnStmt != nil {
                context = "return statement"
        }
 
@@ -449,7 +514,7 @@ func (check *Checker) shortVarDecl(pos syntax.Pos, lhs, rhs []syntax.Expr) {
                }
        }
 
-       check.initVars(lhsVars, rhs, nopos)
+       check.initVars(lhsVars, rhs, nil)
 
        // process function literals in rhs expressions before scope changes
        check.processDelayed(top)
index 4e2c2a29894a0683a4350e1b8ca3a16ba267773b..91e2a8f78342b27eb8e378dc310238fe5a0aab71 100644 (file)
@@ -350,12 +350,25 @@ func (check *Checker) arguments(call *syntax.CallExpr, sig *Signature, targs []T
        }
 
        // check argument count
-       switch {
-       case nargs < npars:
-               check.errorf(call, "not enough arguments in call to %s", call.Fun)
-               return
-       case nargs > npars:
-               check.errorf(args[npars], "too many arguments in call to %s", call.Fun) // report at first extra argument
+       if nargs != npars {
+               var at poser = call
+               qualifier := "not enough"
+               if nargs > npars {
+                       at = args[npars].expr // report at first extra argument
+                       qualifier = "too many"
+               } else if nargs > 0 {
+                       at = args[nargs-1].expr // report at last argument
+               }
+               // take care of empty parameter lists represented by nil tuples
+               var params []*Var
+               if sig.params != nil {
+                       params = sig.params.vars
+               }
+               var err error_
+               err.errorf(at, "%s arguments in call to %s", qualifier, call.Fun)
+               err.errorf(nopos, "have %s", check.typesSummary(operandTypes(args), false))
+               err.errorf(nopos, "want %s", check.typesSummary(varTypes(params), sig.variadic))
+               check.report(&err)
                return
        }
 
index 4b79c59af31f6938658aaa279af2dad16d29150b..a4bc3969c0de1e2ffd44663c8d088c888252ed8f 100644 (file)
@@ -549,7 +549,7 @@ func (check *Checker) varDecl(obj *Var, lhs []*Var, typ, init syntax.Expr) {
                }
        }
 
-       check.initVars(lhs, []syntax.Expr{init}, nopos)
+       check.initVars(lhs, []syntax.Expr{init}, nil)
 }
 
 // isImportedConstraint reports whether typ is an imported type constraint.
index 44d9256c50f1ac231ebe6a6c57f0c35eaa104c7e..ab64882c0273a2c197f4c38fa46ab1810f8d244e 100644 (file)
@@ -493,7 +493,7 @@ func (check *Checker) stmt(ctxt stmtContext, s syntax.Stmt) {
                                }
                        } else {
                                // return has results or result parameters are unnamed
-                               check.initVars(res.vars, results, s.Pos())
+                               check.initVars(res.vars, results, s)
                        }
                } else if len(results) > 0 {
                        check.error(results[0], "no result values expected")
index 17e4068d6546ea22080d5cd3f4bb3049aa119498..de27f5c6322132acbddfd8dc7d84b3fa44e923e7 100644 (file)
@@ -25,7 +25,7 @@ func append1() {
        _ = append(s, b)
        _ = append(s, x /* ERROR cannot use x */ )
        _ = append(s, s /* ERROR cannot use s */ )
-       _ = append(s... ) /* ERROR not enough arguments */
+       _ = append(s /* ERROR not enough arguments */ ...)
        _ = append(s, b, s /* ERROR too many arguments */ ... )
        _ = append(s, 1, 2, 3)
        _ = append(s, 1, 2, 3, x /* ERROR cannot use x */ , 5, 6, 6)
index ff929217c4c865ae18f8b918ee720762f15cf3cc..5f09197bde0f27d56ec3abb0543facc3261d1417 100644 (file)
@@ -8,32 +8,38 @@ package errors
 // (matching messages are regular expressions, hence the \'s).
 func f(x int, m map[string]int) {
        // no values
-       _ = f /* ERROR "f\(0, m\) \(no value\) used as value" */ (0, m)
+       _ = f /* ERROR f\(0, m\) \(no value\) used as value */ (0, m)
 
        // built-ins
-       _ = println /* ERROR "println \(built-in\) must be called" */
+       _ = println // ERROR println \(built-in\) must be called
 
        // types
-       _ = complex128 /* ERROR "complex128 \(type\) is not an expression" */
+       _ = complex128 // ERROR complex128 \(type\) is not an expression
 
        // constants
        const c1 = 991
        const c2 float32 = 0.5
-       0 /* ERROR "0 \(untyped int constant\) is not used" */
-       c1 /* ERROR "c1 \(untyped int constant 991\) is not used" */
-       c2 /* ERROR "c2 \(constant 0.5 of type float32\) is not used" */
-       c1 /* ERROR "c1 \+ c2 \(constant 991.5 of type float32\) is not used" */ + c2
+       const c3 = "foo"
+       0 // ERROR 0 \(untyped int constant\) is not used
+       0.5 // ERROR 0.5 \(untyped float constant\) is not used
+       "foo" // ERROR "foo" \(untyped string constant\) is not used
+       c1 // ERROR c1 \(untyped int constant 991\) is not used
+       c2 // ERROR c2 \(constant 0.5 of type float32\) is not used
+       c1 /* ERROR c1 \+ c2 \(constant 991.5 of type float32\) is not used */ + c2
+       c3 // ERROR c3 \(untyped string constant "foo"\) is not used
 
        // variables
-       x /* ERROR "x \(variable of type int\) is not used" */
+       x // ERROR x \(variable of type int\) is not used
 
        // values
-       x /* ERROR "x != x \(untyped bool value\) is not used" */ != x
-       x /* ERROR "x \+ x \(value of type int\) is not used" */ + x
+       nil // ERROR nil is not used
+       (*int)(nil) // ERROR \(\*int\)\(nil\) \(value of type \*int\) is not used
+       x /* ERROR x != x \(untyped bool value\) is not used */ != x
+       x /* ERROR x \+ x \(value of type int\) is not used */ + x
 
        // value, ok's
        const s = "foo"
-       m /* ERROR "m\[s\] \(map index expression of type int\) is not used" */ [s]
+       m /* ERROR m\[s\] \(map index expression of type int\) is not used */ [s]
 }
 
 // Valid ERROR comments can have a variety of forms.
index 0d7bbae9f9533fdb3ec1830e6c5bc04c08d99e6d..646319e4c470713b760ca5068cc54f7c313e07a8 100644 (file)
@@ -494,23 +494,23 @@ func _calls() {
        f1(0)
        f1(x)
        f1(10.0)
-       f1() /* ERROR "not enough arguments" */
-       f1(x, y /* ERROR "too many arguments" */ )
+       f1 /* ERROR "not enough arguments in call to f1\n\thave \(\)\n\twant \(int\)" */ ()
+       f1(x, y /* ERROR "too many arguments in call to f1\n\thave \(int, float32\)\n\twant \(int\)" */ )
        f1(s /* ERROR "cannot use .* in argument" */ )
        f1(x ... /* ERROR "cannot use ..." */ )
        f1(g0 /* ERROR "used as value" */ ())
        f1(g1())
-       f1(g2 /* ERROR "too many arguments" */ ())
+       f1(g2 /* ERROR "too many arguments in call to f1\n\thave \(float32, string\)\n\twant \(int\)" */ ())
 
-       f2() /* ERROR "not enough arguments" */
-       f2(3.14) /* ERROR "not enough arguments" */
+       f2 /* ERROR "not enough arguments in call to f2\n\thave \(\)\n\twant \(float32, string\)" */ ()
+       f2(3.14 /* ERROR "not enough arguments in call to f2\n\thave \(number\)\n\twant \(float32, string\)" */ )
        f2(3.14, "foo")
        f2(x /* ERROR "cannot use .* in argument" */ , "foo")
        f2(g0 /* ERROR "used as value" */ ())
-       f2(g1()) /* ERROR "not enough arguments" */
+       f2(g1 /* ERROR "not enough arguments in call to f2\n\thave \(int\)\n\twant \(float32, string\)" */ ())
        f2(g2())
 
-       fs() /* ERROR "not enough arguments" */
+       fs /* ERROR "not enough arguments" */ ()
        fs(g0 /* ERROR "used as value" */ ())
        fs(g1 /* ERROR "cannot use .* in argument" */ ())
        fs(g2 /* ERROR "too many arguments" */ ())
index 8171c57d8b57e8c22432b94e08983366a6cf7941..8b18d676ac258513eb68f2a7a254219c3b7ff606 100644 (file)
@@ -29,10 +29,10 @@ func assignments0() (int, int) {
 
        a, b, c = <- /* ERROR "cannot assign [1-9]+ values to [1-9]+ variables" */ ch
 
-       return /* ERROR "wrong number of return values" */
-       return /* ERROR "wrong number of return values" */ 1
+       return /* ERROR "not enough return values\n\thave \(\)\n\twant \(int, int\)" */
+       return 1 /* ERROR "not enough return values\n\thave \(number\)\n\twant \(int, int\)" */
        return 1, 2
-       return /* ERROR "wrong number of return values" */ 1, 2, 3
+       return 1, 2, 3 /* ERROR "too many return values\n\thave \(number, number, number\)\n\twant \(int, int\)" */
 }
 
 func assignments1() {
@@ -81,12 +81,12 @@ func assignments1() {
        // test cases for issue 5500
        _ = func() (int, bool) {
                var m map[int]int
-               return /* ERROR "wrong number of return values" */ m[0]
+               return m /* ERROR "not enough return values" */ [0]
        }
 
        g := func(int, bool){}
        var m map[int]int
-       g(m[0]) /* ERROR "not enough arguments" */
+       g(m /* ERROR "not enough arguments" */ [0])
 
        // assignments to _
        _ = nil /* ERROR "use of untyped nil" */
@@ -380,15 +380,15 @@ func returns0() {
 
 func returns1(x float64) (int, *float64) {
        return 0, &x
-       return /* ERROR wrong number of return values */
+       return /* ERROR not enough return values */
        return "foo" /* ERROR "cannot .* in return statement" */, x /* ERROR "cannot use .* in return statement" */
-       return /* ERROR wrong number of return values */ 0, &x, 1
+       return 0, &x, 1 /* ERROR too many return values */
 }
 
 func returns2() (a, b int) {
        return
        return 1, "foo" /* ERROR cannot use .* in return statement */
-       return /* ERROR wrong number of return values */ 1, 2, 3
+       return 1, 2, 3 /* ERROR too many return values */
        {
                type a int
                return 1, 2
index d72cf078a76f799af2ac9ad8222407a34ad97b78..007157ea0f1d5e6163f760728420a6b261495ae5 100644 (file)
@@ -312,7 +312,7 @@ var _ = f7(1.2, 3 /* ERROR does not match */ )
 
 func f8[A, B any](A, B, ...B) int { panic(0) }
 
-var _ = f8(1) /* ERROR not enough arguments */
+var _ = f8(1 /* ERROR not enough arguments */ )
 var _ = f8(1, 2.3)
 var _ = f8(1, 2.3, 3.4, 4.5)
 var _ = f8(1, 2.3, 3.4, 4 /* ERROR does not match */ )
index 9e48cdf847740ea69125c7cbcab59d81ff03ea74..827b9b9d69c89de58578cff973000fef76fa7564 100644 (file)
@@ -183,7 +183,7 @@ func _() {
 
 func _() int {
        var x, y int
-       return /* ERROR wrong number of return values */ x, y
+       return x, y /* ERROR too many return values */
 }
 
 // Short variable declarations must declare at least one new non-blank variable.
index dfd8be80050edae795a665adb4d3ea9e0d9c902b..74e06f39d78707388830da49486dfe54f1fca84f 100644 (file)
@@ -19,7 +19,7 @@ func h() (_ int, _ error) {
 }
 
 func i() (int, error) {
-       return // ERROR "not enough arguments to return|wrong number of return values"
+       return // ERROR "not enough return values|not enough arguments to return"
 }
 
 func f1() (_ int, err error) {
index 7201591f3f3c851445c7d3d007739acf60359bfa..b6ece4bf21db0e02255f3603a3eeb5d49c1b1815 100644 (file)
@@ -7,7 +7,7 @@
 package main
 
 func foo() (int, int) {
-       return 2.3 // ERROR "not enough arguments to return\n\thave \(number\)\n\twant \(int, int\)|not enough arguments to return|wrong number of return values"
+       return 2.3 // ERROR "not enough return values\n\thave \(number\)\n\twant \(int, int\)|not enough arguments to return"
 }
 
 func foo2() {
@@ -16,19 +16,19 @@ func foo2() {
 
 func foo3(v int) (a, b, c, d int) {
        if v >= 0 {
-               return 1 // ERROR "not enough arguments to return\n\thave \(number\)\n\twant \(int, int, int, int\)|not enough arguments to return|wrong number of return values"
+               return 1 // ERROR "not enough return values\n\thave \(number\)\n\twant \(int, int, int, int\)|not enough arguments to return"
        }
-       return 2, 3 // ERROR "not enough arguments to return\n\thave \(number, number\)\n\twant \(int, int, int, int\)|not enough arguments to return|wrong number of return values"
+       return 2, 3 // ERROR "not enough return values\n\thave \(number, number\)\n\twant \(int, int, int, int\)|not enough arguments to return"
 }
 
 func foo4(name string) (string, int) {
        switch name {
        case "cow":
-               return "moo" // ERROR "not enough arguments to return\n\thave \(string\)\n\twant \(string, int\)|not enough arguments to return|wrong number of return values"
+               return "moo" // ERROR "not enough return values\n\thave \(string\)\n\twant \(string, int\)|not enough arguments to return"
        case "dog":
-               return "dog", 10, true // ERROR "too many arguments to return\n\thave \(string, number, bool\)\n\twant \(string, int\)|too many values in return statement|wrong number of return values"
+               return "dog", 10, true // ERROR "too many return values\n\thave \(string, number, bool\)\n\twant \(string, int\)|too many arguments to return"
        case "fish":
-               return "" // ERROR "not enough arguments to return\n\thave \(string\)\n\twant \(string, int\)|not enough arguments to return|wrong number of return values"
+               return "" // ERROR "not enough return values\n\thave \(string\)\n\twant \(string, int\)|not enough arguments to return"
        default:
                return "lizard", 10
        }
@@ -40,14 +40,14 @@ type U float64
 
 func foo5() (S, T, U) {
        if false {
-               return "" // ERROR "not enough arguments to return\n\thave \(string\)\n\twant \(S, T, U\)|not enough arguments to return|wrong number of return values"
+               return "" // ERROR "not enough return values\n\thave \(string\)\n\twant \(S, T, U\)|not enough arguments to return"
        } else {
                ptr := new(T)
-               return ptr // ERROR "not enough arguments to return\n\thave \(\*T\)\n\twant \(S, T, U\)|not enough arguments to return|wrong number of return values"
+               return ptr // ERROR "not enough return values\n\thave \(\*T\)\n\twant \(S, T, U\)|not enough arguments to return"
        }
-       return new(S), 12.34, 1 + 0i, 'r', true // ERROR "too many arguments to return\n\thave \(\*S, number, number, number, bool\)\n\twant \(S, T, U\)|too many values in return statement|wrong number of return values"
+       return new(S), 12.34, 1 + 0i, 'r', true // ERROR "too many return values\n\thave \(\*S, number, number, number, bool\)\n\twant \(S, T, U\)|too many arguments to return"
 }
 
 func foo6() (T, string) {
-       return "T", true, true // ERROR "too many arguments to return\n\thave \(string, bool, bool\)\n\twant \(T, string\)|too many values in return statement|wrong number of return values"
+       return "T", true, true // ERROR "too many return values\n\thave \(string, bool, bool\)\n\twant \(T, string\)|too many arguments to return"
 }
index f3ed3c3def0c9b2bbb5ced20d84fe4e47f1fb8e0..6c1c0fe0c28981f3dbb19d7859181c55d3cdc9bc 100644 (file)
@@ -9,5 +9,5 @@ package main
 func f(a int, b ...int) {}
 
 func main() {
-       f(nil...) // ERROR "not enough arguments in call to f$"
+       f(nil...) // ERROR "not enough arguments in call to f\n\thave \(nil\)\n\twant \(int, \[\]int\)|not enough arguments"
 }
diff --git a/test/fixedbugs/issue48834.go b/test/fixedbugs/issue48834.go
new file mode 100644 (file)
index 0000000..cf97d13
--- /dev/null
@@ -0,0 +1,24 @@
+// errorcheck
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+func _() (int, error) {
+       return 1 // ERROR "not enough (arguments to return|return values)\n\thave \(number\)\n\twant \(int, error\)"
+}
+
+func _() (int, error) {
+       var x int
+       return x // ERROR "not enough (arguments to return|return values)\n\thave \(int\)\n\twant \(int, error\)"
+}
+
+func _() int {
+       return 1, 2 // ERROR "too many (arguments to return|return values)\n\thave \(number, number\)\n\twant \(int\)"
+}
+
+func _() {
+       return 1 // ERROR "too many arguments to return\n\thave \(number\)\n\twant \(\)|no result values expected"
+}
diff --git a/test/fixedbugs/issue48835.go b/test/fixedbugs/issue48835.go
new file mode 100644 (file)
index 0000000..c000f83
--- /dev/null
@@ -0,0 +1,25 @@
+// errorcheck
+
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+func f0()
+func f1(_ int)
+func f2(_, _ int)
+func f2ddd(_, _ int, _ ...int)
+
+func f() {
+       var x int
+       f0(1)              // ERROR "too many arguments in call to f0\n\thave \(number\)\n\twant \(\)"
+       f0(x)              // ERROR "too many arguments in call to f0\n\thave \(int\)\n\twant \(\)"
+       f1()               // ERROR "not enough arguments in call to f1\n\thave \(\)\n\twant \(int\)"
+       f1(1, 2)           // ERROR "too many arguments in call to f1\n\thave \(number, number\)\n\twant \(int\)"
+       f2(1)              // ERROR "not enough arguments in call to f2\n\thave \(number\)\n\twant \(int, int\)"
+       f2(1, "foo", true) // ERROR "too many arguments in call to f2\n\thave \(number, string, bool\)\n\twant \(int, int\)"
+       f2ddd(1)           // ERROR "not enough arguments in call to f2ddd\n\thave \(number\)\n\twant \(int, int, \.\.\.int\)"
+       f2ddd(1, 2)
+       f2ddd(1, 2, 3)
+}