diff --git a/build/lint-locale-usage/handle-go.go b/build/lint-locale-usage/handle-go.go index 1028d08e8e..af97acf1c9 100644 --- a/build/lint-locale-usage/handle-go.go +++ b/build/lint-locale-usage/handle-go.go @@ -20,7 +20,11 @@ func (handler Handler) handleGoTrBasicLit(fset *token.FileSet, argLit *ast.Basic if err == nil { // found interesting strings if strings.HasSuffix(arg, ".") || strings.HasSuffix(arg, "_") { - handler.OnMsgidPrefix(fset, argLit.ValuePos, arg) + prep, trunc := PrepareMsgidPrefix(arg) + if trunc { + handler.OnWarning(fset, argLit.ValuePos, fmt.Sprintf("needed to truncate message id prefix: %s", arg)) + } + handler.OnMsgidPrefix(fset, argLit.ValuePos, prep) } else { handler.OnMsgid(fset, argLit.ValuePos, arg) } diff --git a/build/lint-locale-usage/handle-tmpl.go b/build/lint-locale-usage/handle-tmpl.go index cbf60d675f..ae64d796f1 100644 --- a/build/lint-locale-usage/handle-tmpl.go +++ b/build/lint-locale-usage/handle-tmpl.go @@ -5,8 +5,10 @@ package main import ( + "fmt" "go/token" "os" + "strings" "text/template" tmplParser "text/template/parse" @@ -68,12 +70,7 @@ func (handler Handler) handleTemplateNode(fset *token.FileSet, node tmplParser.N for _, argNum := range ltf { if len(nodeCommand.Args) >= int(argNum+2) { - nodeString, ok := nodeCommand.Args[int(argNum+1)].(*tmplParser.StringNode) - if ok { - // found interesting strings - // the column numbers are a bit "off", but much better than nothing - handler.OnMsgid(fset, token.Pos(nodeString.Pos), nodeString.Text) - } + handler.handleTemplateMsgid(fset, nodeCommand.Args[int(argNum+1)]) } else { argc := len(nodeCommand.Args) - 1 gotUnexpectedInvoke = &argc @@ -88,6 +85,82 @@ func (handler Handler) handleTemplateNode(fset *token.FileSet, node tmplParser.N } } +func (handler Handler) handleTemplateMsgid(fset *token.FileSet, node tmplParser.Node) { + // the column numbers are a bit "off", but much better than nothing + pos := token.Pos(node.Position()) + + switch node.Type() { + case tmplParser.NodeString: + nodeString := node.(*tmplParser.StringNode) + // found interesting strings + handler.OnMsgid(fset, pos, nodeString.Text) + + case tmplParser.NodePipe: + nodePipe := node.(*tmplParser.PipeNode) + handler.handleTemplatePipeNode(fset, nodePipe) + + if len(nodePipe.Cmds) == 0 { + handler.OnWarning(fset, pos, fmt.Sprintf("unsupported invocation of locate function (no commands): %s", node.String())) + } else if len(nodePipe.Cmds) != 1 { + handler.OnWarning(fset, pos, fmt.Sprintf("unsupported invocation of locate function (too many commands): %s", node.String())) + return + } + nodeCommand := nodePipe.Cmds[0] + if len(nodeCommand.Args) < 2 { + handler.OnWarning(fset, pos, fmt.Sprintf("unsupported invocation of locate function (not enough arguments): %s", node.String())) + return + } + + nodeIdent, ok := nodeCommand.Args[0].(*tmplParser.IdentifierNode) + if !ok || (nodeIdent.Ident != "print" && nodeIdent.Ident != "printf") { + // handler.OnWarning(fset, pos, fmt.Sprintf("unsupported invocation of locate function (bad command): %s", node.String())) + return + } + + nodeString, ok := nodeCommand.Args[1].(*tmplParser.StringNode) + if !ok { + //handler.OnWarning( + // fset, + // pos, + // fmt.Sprintf("unsupported invocation of locate function (string should be first argument to %s): %s", nodeIdent.Ident, node.String()), + //) + return + } + + msgidPrefix := nodeString.Text + stringPos := token.Pos(nodeString.Pos) + + if len(nodeCommand.Args) == 2 { + // found interesting strings + handler.OnMsgid(fset, stringPos, msgidPrefix) + } else { + if nodeIdent.Ident == "printf" { + parts := strings.SplitN(msgidPrefix, "%", 2) + if len(parts) != 2 { + handler.OnWarning( + fset, + stringPos, + fmt.Sprintf("unsupported invocation of locate function (format string doesn't match \"prefix%%smth\" pattern): %s", nodeString.String()), + ) + return + } + msgidPrefix = parts[0] + } + + msgidPrefixFin, truncated := PrepareMsgidPrefix(msgidPrefix) + if truncated { + handler.OnWarning(fset, stringPos, fmt.Sprintf("needed to truncate message id prefix: %s", msgidPrefix)) + } + + // found interesting strings + handler.OnMsgidPrefix(fset, stringPos, msgidPrefixFin) + } + + default: + // handler.OnWarning(fset, pos, fmt.Sprintf("unknown invocation of locate function: %s", node.String())) + } +} + func (handler Handler) handleTemplatePipeNode(fset *token.FileSet, pipeNode *tmplParser.PipeNode) { if pipeNode == nil { return diff --git a/build/lint-locale-usage/lint-locale-usage.go b/build/lint-locale-usage/lint-locale-usage.go index 3a181bd253..dba03587a2 100644 --- a/build/lint-locale-usage/lint-locale-usage.go +++ b/build/lint-locale-usage/lint-locale-usage.go @@ -161,6 +161,15 @@ func ParseAllowedMaskedUsages(fname string, usedMsgids *container.Set[string], a return nil } +// Truncating a message id prefix to the last dot +func PrepareMsgidPrefix(s string) (string, bool) { + index := strings.LastIndexByte(s, 0x2e) + if index == -1 { + return "", true + } + return s[:index], index != len(s)-1 +} + // This command assumes that we get started from the project root directory // // Possible command line flags: