message/pipeline: hoist CallExpr handling
Change-Id: I01ed5f06814dc71268407e302bebeefd7b608b8f
Reviewed-on: https://go-review.googlesource.com/105015
Run-TryBot: Marcel van Lohuizen <mpvl@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ross Light <light@google.com>
diff --git a/message/pipeline/extract.go b/message/pipeline/extract.go
index e006216..327670f 100644
--- a/message/pipeline/extract.go
+++ b/message/pipeline/extract.go
@@ -348,138 +348,42 @@
}
}
-func (x *extracter) extractMessages() {
- // print returns Go syntax for the specified node.
- print := func(n ast.Node) string {
- var buf bytes.Buffer
- format.Node(&buf, x.conf.Fset, n)
- return buf.String()
+// print returns Go syntax for the specified node.
+func (x *extracter) print(n ast.Node) string {
+ var buf bytes.Buffer
+ format.Node(&buf, x.conf.Fset, n)
+ return buf.String()
+}
+
+type packageExtracter struct {
+ x *extracter
+ info *loader.PackageInfo
+ cmap ast.CommentMap
+}
+
+func (px packageExtracter) getComment(n ast.Node) string {
+ cs := px.cmap.Filter(n).Comments()
+ if len(cs) > 0 {
+ return strings.TrimSpace(cs[0].Text())
}
+ return ""
+}
+
+func (x *extracter) extractMessages() {
prog := x.iprog
for _, info := range x.iprog.AllPackages {
for _, f := range info.Files {
// Associate comments with nodes.
- cmap := ast.NewCommentMap(prog.Fset, f, f.Comments)
- getComment := func(n ast.Node) string {
- cs := cmap.Filter(n).Comments()
- if len(cs) > 0 {
- return strings.TrimSpace(cs[0].Text())
- }
- return ""
+ px := packageExtracter{
+ x, info,
+ ast.NewCommentMap(prog.Fset, f, f.Comments),
}
// Find function calls.
ast.Inspect(f, func(n ast.Node) bool {
- call, ok := n.(*ast.CallExpr)
- if !ok {
- return true
- }
- data := x.funcs[call.Lparen]
- if data == nil || len(data.formats) == 0 {
- return true
- }
- x.debug(data.call, "INSERT", data.formats)
-
- argn := data.callFormatPos()
- if argn >= len(call.Args) {
- return true
- }
- format := call.Args[argn]
-
- comment := ""
- key := []string{}
- if ident, ok := format.(*ast.Ident); ok {
- key = append(key, ident.Name)
- if v, ok := ident.Obj.Decl.(*ast.ValueSpec); ok && v.Comment != nil {
- // TODO: get comment above ValueSpec as well
- comment = v.Comment.Text()
- }
- }
-
- arguments := []argument{}
- simArgs := []interface{}{}
- if data.callArgsStart() >= 0 {
- args := call.Args[data.callArgsStart():]
- simArgs = make([]interface{}, len(args))
- for i, arg := range args {
- expr := print(arg)
- val := ""
- if v := info.Types[arg].Value; v != nil {
- val = v.ExactString()
- simArgs[i] = val
- switch arg.(type) {
- case *ast.BinaryExpr, *ast.UnaryExpr:
- expr = val
- }
- }
- arguments = append(arguments, argument{
- ArgNum: i + 1,
- Type: info.Types[arg].Type.String(),
- UnderlyingType: info.Types[arg].Type.Underlying().String(),
- Expr: expr,
- Value: val,
- Comment: getComment(arg),
- Position: posString(&x.conf, info.Pkg, arg.Pos()),
- // TODO report whether it implements
- // interfaces plural.Interface,
- // gender.Interface.
- })
- }
- }
-
- formats := data.formats
- for _, c := range formats {
- key := append([]string{}, key...)
- fmtMsg := constant.StringVal(c)
- msg := ""
-
- ph := placeholders{index: map[string]string{}}
-
- trimmed, _, _ := trimWS(fmtMsg)
-
- p := fmtparser.Parser{}
- p.Reset(simArgs)
- for p.SetFormat(trimmed); p.Scan(); {
- switch p.Status {
- case fmtparser.StatusText:
- msg += p.Text()
- case fmtparser.StatusSubstitution,
- fmtparser.StatusBadWidthSubstitution,
- fmtparser.StatusBadPrecSubstitution:
- arguments[p.ArgNum-1].used = true
- arg := arguments[p.ArgNum-1]
- sub := p.Text()
- if !p.HasIndex {
- r, sz := utf8.DecodeLastRuneInString(sub)
- sub = fmt.Sprintf("%s[%d]%c", sub[:len(sub)-sz], p.ArgNum, r)
- }
- msg += fmt.Sprintf("{%s}", ph.addArg(&arg, sub))
- }
- }
- key = append(key, msg)
-
- // Add additional Placeholders that can be used in translations
- // that are not present in the string.
- for _, arg := range arguments {
- if arg.used {
- continue
- }
- ph.addArg(&arg, fmt.Sprintf("%%[%d]v", arg.ArgNum))
- }
-
- if c := getComment(call.Args[0]); c != "" {
- comment = c
- }
-
- x.messages = append(x.messages, Message{
- ID: key,
- Key: fmtMsg,
- Message: Text{Msg: msg},
- // TODO(fix): this doesn't get the before comment.
- Comment: comment,
- Placeholders: ph.slice,
- Position: posString(&x.conf, info.Pkg, call.Lparen),
- })
+ switch v := n.(type) {
+ case *ast.CallExpr:
+ return px.handleCall(v)
}
return true
})
@@ -487,6 +391,119 @@
}
}
+func (px packageExtracter) handleCall(call *ast.CallExpr) bool {
+ x := px.x
+ info := px.info
+ data := x.funcs[call.Lparen]
+ if data == nil || len(data.formats) == 0 {
+ return true
+ }
+ x.debug(data.call, "INSERT", data.formats)
+
+ argn := data.callFormatPos()
+ if argn >= len(call.Args) {
+ return true
+ }
+ format := call.Args[argn]
+
+ comment := ""
+ key := []string{}
+ if ident, ok := format.(*ast.Ident); ok {
+ key = append(key, ident.Name)
+ if v, ok := ident.Obj.Decl.(*ast.ValueSpec); ok && v.Comment != nil {
+ // TODO: get comment above ValueSpec as well
+ comment = v.Comment.Text()
+ }
+ }
+
+ arguments := []argument{}
+ simArgs := []interface{}{}
+ if data.callArgsStart() >= 0 {
+ args := call.Args[data.callArgsStart():]
+ simArgs = make([]interface{}, len(args))
+ for i, arg := range args {
+ expr := x.print(arg)
+ val := ""
+ if v := info.Types[arg].Value; v != nil {
+ val = v.ExactString()
+ simArgs[i] = val
+ switch arg.(type) {
+ case *ast.BinaryExpr, *ast.UnaryExpr:
+ expr = val
+ }
+ }
+ arguments = append(arguments, argument{
+ ArgNum: i + 1,
+ Type: info.Types[arg].Type.String(),
+ UnderlyingType: info.Types[arg].Type.Underlying().String(),
+ Expr: expr,
+ Value: val,
+ Comment: px.getComment(arg),
+ Position: posString(&x.conf, info.Pkg, arg.Pos()),
+ // TODO report whether it implements
+ // interfaces plural.Interface,
+ // gender.Interface.
+ })
+ }
+ }
+
+ formats := data.formats
+ for _, c := range formats {
+ key := append([]string{}, key...)
+ fmtMsg := constant.StringVal(c)
+ msg := ""
+
+ ph := placeholders{index: map[string]string{}}
+
+ trimmed, _, _ := trimWS(fmtMsg)
+
+ p := fmtparser.Parser{}
+ p.Reset(simArgs)
+ for p.SetFormat(trimmed); p.Scan(); {
+ switch p.Status {
+ case fmtparser.StatusText:
+ msg += p.Text()
+ case fmtparser.StatusSubstitution,
+ fmtparser.StatusBadWidthSubstitution,
+ fmtparser.StatusBadPrecSubstitution:
+ arguments[p.ArgNum-1].used = true
+ arg := arguments[p.ArgNum-1]
+ sub := p.Text()
+ if !p.HasIndex {
+ r, sz := utf8.DecodeLastRuneInString(sub)
+ sub = fmt.Sprintf("%s[%d]%c", sub[:len(sub)-sz], p.ArgNum, r)
+ }
+ msg += fmt.Sprintf("{%s}", ph.addArg(&arg, sub))
+ }
+ }
+ key = append(key, msg)
+
+ // Add additional Placeholders that can be used in translations
+ // that are not present in the string.
+ for _, arg := range arguments {
+ if arg.used {
+ continue
+ }
+ ph.addArg(&arg, fmt.Sprintf("%%[%d]v", arg.ArgNum))
+ }
+
+ if c := px.getComment(call.Args[0]); c != "" {
+ comment = c
+ }
+
+ x.messages = append(x.messages, Message{
+ ID: key,
+ Key: fmtMsg,
+ Message: Text{Msg: msg},
+ // TODO(fix): this doesn't get the before comment.
+ Comment: comment,
+ Placeholders: ph.slice,
+ Position: posString(&x.conf, info.Pkg, call.Lparen),
+ })
+ }
+ return true
+}
+
func posString(conf *loader.Config, pkg *types.Package, pos token.Pos) string {
p := conf.Fset.Position(pos)
file := fmt.Sprintf("%s:%d:%d", filepath.Base(p.Filename), p.Line, p.Column)