in warn/warn_docstring.go [270:374]
func functionDocstringArgsWarning(f *build.File) []*LinterFinding {
var findings []*LinterFinding
build.WalkStatements(f, func(expr build.Expr, stack []build.Expr) (err error) {
def, ok := expr.(*build.DefStmt)
if !ok {
return
}
doc, ok := getDocstring(def.Body)
if !ok {
return
}
info := parseFunctionDocstring((*doc).(*build.StringExpr))
if info.argumentsPos.LineRune > 0 {
argumentsEnd := info.argumentsPos
argumentsEnd.LineRune += len("Arguments:")
argumentsEnd.Byte += len("Arguments:")
finding := makeLinterFinding(*doc, `Prefer "Args:" to "Arguments:" when documenting function arguments.`)
finding.Start = info.argumentsPos
finding.End = argumentsEnd
findings = append(findings, finding)
}
if !isDocstringRequired(def) && len(info.args) == 0 {
return
}
// If a docstring is required or there are any arguments described, check for their integrity.
// Check whether all arguments are documented.
notDocumentedArguments := []string{}
paramNames := make(map[string]bool)
for _, param := range def.Params {
name, op := build.GetParamName(param)
if name == "" {
continue
}
name = op + name // *args or **kwargs
paramNames[name] = true
if _, ok := info.args[name]; !ok {
notDocumentedArguments = append(notDocumentedArguments, name)
}
}
// Check whether all existing arguments are commented
if len(notDocumentedArguments) > 0 {
message := fmt.Sprintf("Argument %q is not documented.", notDocumentedArguments[0])
plural := ""
if len(notDocumentedArguments) > 1 {
message = fmt.Sprintf(
`Arguments "%s" are not documented.`,
strings.Join(notDocumentedArguments, `", "`),
)
plural = "s"
}
if len(info.args) == 0 {
// No arguments are documented maybe the Args: block doesn't exist at all or
// formatted improperly. Add extra information to the warning message
message += fmt.Sprintf(`
If the documentation for the argument%s exists but is not recognized by Buildifier
make sure it follows the line "Args:" which has the same indentation as the opening """,
and the argument description starts with "<argument_name>:" and indented with at least
one (preferably two) space more than "Args:", for example:
def %s(%s):
"""Function description.
Args:
%s: argument description, can be
multiline with additional indentation.
"""`, plural, def.Name, notDocumentedArguments[0], notDocumentedArguments[0])
}
findings = append(findings, makeLinterFinding(*doc, message))
}
// Check whether all documented arguments actually exist in the function signature.
for name, pos := range info.args {
if paramNames[name] {
continue
}
msg := fmt.Sprintf("Argument %q is documented but doesn't exist in the function signature.", name)
// *args and **kwargs should be documented with asterisks
for _, asterisks := range []string{"*", "**"} {
if paramNames[asterisks+name] {
msg += fmt.Sprintf(` Do you mean "%s%s"?`, asterisks, name)
break
}
}
posEnd := pos
posEnd.LineRune += len(name)
finding := makeLinterFinding(*doc, msg)
finding.Start = pos
finding.End = posEnd
findings = append(findings, finding)
}
return
})
return findings
}