Search code examples
goformattingvariadic-functions

Is a formatting directive expected in variadic functions?


I have a variadic function respond that takes several positional arguments and an any slice spread at the end. Depending on the context, the function uses the last parameter in different ways (note how message and data differ depending on the value of status.):

func respond(w http.ResponseWriter, status int, format string, a ...any) {
    var (
        statusOk bool
        message  string
        data     interface{}
    )
    if status >= 300 {
        statusOk = false
        message = fmt.Sprintf(format, a...)
        data = nil
        log.Error().Msg(message)
    } else {
        statusOk = true
        message = format
        data = a[0]
        log.Info().Msg(fmt.Sprintf("%d", status))
    }
    // issue response
    responder := render.New()
    responder.JSON(w, status, map[string]interface{}{
        "statusOk": statusOk,
        "status":   status,
        "message":  message,
        "data":     data,
    })
}

For the call below I do not get any warnings:

respond(w, http.StatusNotFound, "person %v does not exist", personId)

For the next one, however, the warning respond call has arguments but no formatting directives is raised (but the code runs as expected, person is a struct):

respond(w, http.StatusAccepted, "updated", person)

To my amateur eye, it looks like the variadic function expects to have a formatting string and parameters for this format at the end. But why would that be the case? Is there a limitation to what a variadic function is supposed to do?

Or should I better split responder in two, one for each case ("ok" and "not ok")?


Also see https://github.com/golang/go/issues/26486 and https://go.dev/src/cmd/vet/testdata/print/print.go (line 316) for a discussion of that message in Go's code (what I have is a warning from a linter, as @mkopriva and @JimB mentioned in their comments)


Solution

  • The warning comes from the linter because you're calling the respond() function with a format string not matching the arguments provided after that.

    This specific message comes from the printf Analyzer which documents that:

    printf: check consistency of Printf format strings and arguments

    The check applies to calls of the formatting functions such as fmt.Printf and fmt.Sprintf, as well as any detected wrappers of those functions.

    Since your response() function uses the format and a parameters to call fmt.Sprintf() as-is, this is considered as a wrapper for fmt.Sprintf().

    If you want to avoid / get rid of this warning message, the easiest "fix" is to not use those parameters as-is, e.g. make a copy of one of the arguments, and pass that:

    // Make and use a copy to avoid being marked as a wrapper of fmt.Sprintf
    format := format
    message = fmt.Sprintf(format, a...)