Skip to content

Commit

Permalink
Support for inline functions
Browse files Browse the repository at this point in the history
  • Loading branch information
vorlif committed Jun 15, 2022
1 parent 45bffb5 commit 06d1acd
Show file tree
Hide file tree
Showing 18 changed files with 221 additions and 65 deletions.
6 changes: 3 additions & 3 deletions commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ func init() {
fs.BoolVarP(&extractCfg.ExtractErrors, "extract-errors", "e", def.ExtractErrors, "Strings from errors.New(STRING) are extracted")
fs.StringVar(&extractCfg.ErrorContext, "errors-context", def.ErrorContext, "Context which is automatically assigned to extracted errors")

fs.StringArrayVarP(&extractCfg.TemplatePatterns, "template-directory", "t", def.TemplatePatterns, "Set a list of paths to which the template files contain. Regular expressions can be used.")
fs.String("template-prefix", ".T", "Sets a prefix for the translation functions, which is used within the templates")
fs.StringArrayVarP(&extractCfg.TemplatePatterns, "template-directory", "t", []string{}, "Set a list of paths to which the template files contain. Regular expressions can be used.")
fs.String("template-prefix", "", "Sets a prefix for the translation functions, which is used within the templates")
fs.BoolVar(&extractCfg.TmplIsMonolingual, "template-use-kv", false, "Determines whether the strings from templates should be handled as key-value")
fs.StringArrayP("template-keyword", "k", []string{}, "Sets a keyword that is used within templates to identify translation functions")

Expand All @@ -77,7 +77,7 @@ func validateExtractConfig(cmd *cobra.Command) {
fs := cmd.Flags()
if keywordPrefix, errP := fs.GetString("template-prefix"); errP != nil {
log.WithError(errP).Fatal("Args could not be parsed")
} else {
} else if keywordPrefix != "" {
extractCfg.Keywords = tmpl.DefaultKeywords(keywordPrefix, extractCfg.TmplIsMonolingual)
}

Expand Down
5 changes: 2 additions & 3 deletions extract/etype/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ type Token string
const (
None Token = ""
Singular Token = "Singular"
Key Token = "Key"
PluralKey Token = "PluralKey"
Plural Token = "Plural"
Domain Token = "Domain"
Context Token = "Context"
Message Token = "Message"
Key Token = "Key"
PluralKey Token = "PluralKey"
)

func IsMessageID(tok Token) bool {
Expand Down
35 changes: 23 additions & 12 deletions extract/extractors/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,26 +199,29 @@ type SearchResult struct {

func (c *Context) SearchStrings(startExpr ast.Expr) []*SearchResult {
results := make([]*SearchResult, 0)
found := make(map[ast.Node]struct{})

// String was created at the current position
extracted, originNode := ExtractStringLiteral(startExpr)
if extracted != "" {
results = append(results, &SearchResult{Raw: extracted, Node: originNode})
found[originNode] = struct{}{}
}

// Backtracking the string
startIdent, ok := startExpr.(*ast.Ident)
if !ok || startIdent.Obj == nil {
return results
}
mainMessage := extracted

c.Inspector.WithStack([]ast.Node{&ast.AssignStmt{}}, func(raw ast.Node, push bool, stack []ast.Node) (proceed bool) {
proceed = false

node := raw.(*ast.AssignStmt)
if len(node.Lhs) != len(node.Rhs) || len(node.Lhs) == 0 {
return
}

for i, left := range node.Lhs {
leftIdent, isIdent := left.(*ast.Ident)
if !isIdent {
Expand All @@ -227,9 +230,15 @@ func (c *Context) SearchStrings(startExpr ast.Expr) []*SearchResult {
if leftIdent.Obj != startIdent.Obj {
continue
}

if _, ok := found[node.Rhs[i]]; ok {
continue
}

extracted, originNode = ExtractStringLiteral(node.Rhs[i])
if extracted != "" && extracted != mainMessage {
results = append(results, &SearchResult{Raw: extracted, Node: node})
if extracted != "" {
found[node.Rhs[i]] = struct{}{}
results = append(results, &SearchResult{Raw: extracted, Node: originNode})
}

}
Expand All @@ -242,25 +251,27 @@ func (c *Context) SearchStrings(startExpr ast.Expr) []*SearchResult {
// GetComments extracts the Go comments for a list of nodes.
func (c *Context) GetComments(pkg *packages.Package, nodes ...ast.Node) []string {
var comments []string

if _, hasPkg := c.CommentMaps[pkg.PkgPath]; !hasPkg {
return comments
}

found := make(map[*ast.CommentGroup]bool)

for _, node := range nodes {
pos := c.GetPosition(node.Pos())
if _, hasFile := c.CommentMaps[pkg.PkgPath][pos.Filename]; !hasFile {
break
}

c.Inspector.WithStack([]ast.Node{node}, func(n ast.Node, push bool, stack []ast.Node) (proceed bool) {
proceed = false
// Search stack for our node
if n != node {
return
}

if _, hasPkg := c.CommentMaps[pkg.PkgPath]; !hasPkg {
return
}

// Find the first node of the line
pos := c.GetPosition(node.Pos())
if _, hasFile := c.CommentMaps[pkg.PkgPath][pos.Filename]; !hasFile {
return
}

var topNode = node
for i := len(stack) - 1; i >= 0; i-- {
entry := stack[i]
Expand Down
1 change: 1 addition & 0 deletions extract/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func NewRunner(cfg *config.Config, _ map[string]*packages.Package) (*Runner, err
p = append(p,
processors.NewCommentCleaner(cfg),
processors.NewSkipIgnore(),
processors.NewUnprintableCheck(),
processors.NewPrepareKey(),
)

Expand Down
104 changes: 63 additions & 41 deletions goextractors/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ func (de *definitionExtractorRunner) searchDefinitions(n ast.Node, push bool) bo
switch v := n.(type) {
case *ast.FuncDecl:
de.extractFunc(v)
case *ast.AssignStmt:
de.extractInlineFunc(v)
case *ast.GenDecl:
switch v.Tok {
case token.VAR:
Expand Down Expand Up @@ -178,58 +180,78 @@ func (de *definitionExtractorRunner) extractStruct(decl *ast.GenDecl) {
// func translate(msgid localize.Singular, plural localize.Plural)
// func getTranslation() (localize.Singular, localize.Plural).
func (de *definitionExtractorRunner) extractFunc(decl *ast.FuncDecl) {
pck, obj := de.extractCtx.GetType(decl.Name)
if pck == nil {
if decl.Type == nil || decl.Type.Params == nil {
return
}

if decl.Type == nil {
de.extractFunctionsParams(decl.Name, decl.Type)
}

func (de *definitionExtractorRunner) extractInlineFunc(assign *ast.AssignStmt) {
if len(assign.Lhs) == 0 || len(assign.Lhs) != len(assign.Rhs) {
return
}

// function call
if decl.Type.Params != nil {
for i, param := range decl.Type.Params.List {
tok, _ := de.extractCtx.SearchIdentAndToken(param)
if tok == etype.None || tok == etype.Message {
continue
}
ident, ok := assign.Lhs[0].(*ast.Ident)
if !ok {
return
}

if len(param.Names) == 0 {
def := &extractors.Definition{
Type: extractors.FunctionParam,
Token: tok,
Pck: pck,
Ident: decl.Name,
Path: obj.Pkg().Path(),
ID: util.ObjToKey(obj),
Obj: obj,
FieldIdent: nil,
FieldName: strconv.Itoa(i),
funcLit, ok := assign.Rhs[0].(*ast.FuncLit)
if !ok || funcLit.Type == nil || funcLit.Type.Params == nil {
return
}

FieldPos: i,
IsVariadic: isEllipsis(param.Type),
}
de.addDefinition(def)
}
de.extractFunctionsParams(ident, funcLit.Type)
}

for ii, name := range param.Names {
def := &extractors.Definition{
Type: extractors.FunctionParam,
Token: tok,
Pck: pck,
Ident: decl.Name,
Path: obj.Pkg().Path(),
ID: util.ObjToKey(obj),
Obj: obj,
FieldIdent: name,
FieldName: name.Name,
IsVariadic: isEllipsis(param.Type),
func (de *definitionExtractorRunner) extractFunctionsParams(ident *ast.Ident, t *ast.FuncType) {
pck, obj := de.extractCtx.GetType(ident)
if pck == nil {
return
}

FieldPos: calculatePosIdx(i, ii),
}
de.addDefinition(def)
// function call
for i, param := range t.Params.List {
tok, _ := de.extractCtx.SearchIdentAndToken(param)
if tok == etype.None {
continue
}

if len(param.Names) == 0 {
def := &extractors.Definition{
Type: extractors.FunctionParam,
Token: tok,
Pck: pck,
Ident: ident,
Path: obj.Pkg().Path(),
ID: util.ObjToKey(obj),
Obj: obj,
FieldIdent: nil,
FieldName: strconv.Itoa(i),

FieldPos: i,
IsVariadic: isEllipsis(param.Type),
}
de.addDefinition(def)
}

for ii, name := range param.Names {
def := &extractors.Definition{
Type: extractors.FunctionParam,
Token: tok,
Pck: pck,
Ident: ident,
Path: obj.Pkg().Path(),
ID: util.ObjToKey(obj),
Obj: obj,
FieldIdent: name,
FieldName: name.Name,
IsVariadic: isEllipsis(param.Type),

FieldPos: calculatePosIdx(i, ii),
}
de.addDefinition(def)
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion goextractors/funccall.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ func (v funcCallExtractor) Run(_ context.Context, extractCtx *extractors.Context

issues = append(issues, issue)
}

} else if tok != etype.None {
writeMissingMessageID(extractCtx.GetPosition(ident.Pos()), tok, "")
}

funcParameterDefs := extractCtx.Definitions.GetFields(util.ObjToKey(obj))
Expand Down Expand Up @@ -105,6 +106,7 @@ func (v funcCallExtractor) Run(_ context.Context, extractCtx *extractors.Context
}
}

collector.CheckMissingMessageID(extractCtx)
for i, singularResult := range collector.Singulars {
issue := result.Issue{
FromExtractor: v.Name(),
Expand Down
3 changes: 2 additions & 1 deletion goextractors/funccall_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func TestFuncCallExtractor(t *testing.T) {
assert.NotEmpty(t, issues)

want := []string{
// "f-msgid", "f-plural", "f-context", "f-domain",
"f-msgid", "f-plural", "f-context", "f-domain",
"init", "localizer func call",
"noop-msgid", "noop-plural", "noop-context", "noop-domain",
"msgid",
Expand All @@ -28,6 +28,7 @@ func TestFuncCallExtractor(t *testing.T) {
"no-param-msgid", "no-param-plural",
"multi-names-a", "multi-names-b",
"init backtrace", "assign backtrace",
"inline function",
}
got := collectIssueStrings(issues)
assert.ElementsMatch(t, want, got)
Expand Down
3 changes: 2 additions & 1 deletion goextractors/funcreturn.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (e *funcReturnExtractor) Run(_ context.Context, extractCtx *extractors.Cont
extractedResults := make([]etype.Token, len(node.Type.Results.List))
var foundType bool
for i, res := range node.Type.Results.List {
if tok, _ := extractCtx.SearchIdentAndToken(res); tok == etype.None || tok == etype.Message {
if tok, _ := extractCtx.SearchIdentAndToken(res); tok == etype.None {
extractedResults[i] = etype.None
continue
} else {
Expand Down Expand Up @@ -86,6 +86,7 @@ func (e *funcReturnExtractor) Run(_ context.Context, extractCtx *extractors.Cont
}
}

collector.CheckMissingMessageID(extractCtx)
for i, singularResult := range collector.Singulars {
issue := result.Issue{
FromExtractor: e.Name(),
Expand Down
3 changes: 3 additions & 0 deletions goextractors/globalassign.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ func (v globalAssignExtractor) Run(_ context.Context, extractCtx *extractors.Con

tok := extractCtx.GetLocalizeTypeToken(selector)
if tok != etype.Singular {
if tok != etype.None {
writeMissingMessageID(extractCtx.GetPosition(selector.Pos()), tok, "")
}
return
}

Expand Down
2 changes: 2 additions & 0 deletions goextractors/mapsdef.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ func (v mapsDefExtractor) Run(_ context.Context, extractCtx *extractors.Context)
}

continue
} else if token != etype.None {
writeMissingMessageID(extractCtx.GetPosition(ident.Pos()), token, "")
}

// Array of structs
Expand Down
2 changes: 2 additions & 0 deletions goextractors/slicedef.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ func (v sliceDefExtractor) Run(_ context.Context, extractCtx *extractors.Context
}

return
} else if token != etype.None {
writeMissingMessageID(extractCtx.GetPosition(node.Pos()), token, "")
}

structAttr := extractCtx.Definitions.GetFields(util.ObjToKey(obj))
Expand Down
1 change: 1 addition & 0 deletions goextractors/structdef.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ func extractStruct(extractCtx *extractors.Context, node *ast.CompositeLit, obj t
}
}

collector.CheckMissingMessageID(extractCtx)
for i, singularResult := range collector.Singulars {
issue := result.Issue{
FromExtractor: "extract_struct",
Expand Down
5 changes: 3 additions & 2 deletions goextractors/testlib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package goextractors

import (
"context"
"go/ast"
"go/parser"
"go/token"
"path/filepath"
Expand All @@ -21,12 +22,12 @@ const (

func TestPrintAst(t *testing.T) {
fset := token.NewFileSet() // positions are relative to fset
_, err := parser.ParseFile(fset, filepath.Join(testdataDir, "slices.go"), nil, 0)
f, err := parser.ParseFile(fset, filepath.Join(testdataDir, "funccall.go"), nil, 0)
if err != nil {
panic(err)
}

// ast.Print(fset, f)
ast.Print(fset, f)
}

func runExtraction(t *testing.T, dir string, testExtractors ...extractors.Extractor) []result.Issue {
Expand Down
Loading

0 comments on commit 06d1acd

Please sign in to comment.