forked from lug/matterbridge
		
	
		
			
				
	
	
		
			151 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package main
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"go/ast"
 | |
| 	"go/build"
 | |
| 	"go/parser"
 | |
| 	"go/token"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"strings"
 | |
| )
 | |
| 
 | |
| func badArgument(fileset *token.FileSet, p token.Pos) {
 | |
| 	pos := fileset.Position(p)
 | |
| 	filename := pos.Filename
 | |
| 	base, err := os.Getwd()
 | |
| 	if err == nil {
 | |
| 		rpath, perr := filepath.Rel(base, pos.Filename)
 | |
| 		if perr == nil {
 | |
| 			filename = rpath
 | |
| 		}
 | |
| 	}
 | |
| 	msg := fmt.Sprintf("%s:%d: Error: found call to rice.FindBox, "+
 | |
| 		"but argument must be a string literal.\n", filename, pos.Line)
 | |
| 	fmt.Println(msg)
 | |
| 	os.Exit(1)
 | |
| }
 | |
| 
 | |
| func findBoxes(pkg *build.Package) map[string]bool {
 | |
| 	// create map of boxes to embed
 | |
| 	var boxMap = make(map[string]bool)
 | |
| 
 | |
| 	// create one list of files for this package
 | |
| 	filenames := make([]string, 0, len(pkg.GoFiles)+len(pkg.CgoFiles))
 | |
| 	filenames = append(filenames, pkg.GoFiles...)
 | |
| 	filenames = append(filenames, pkg.CgoFiles...)
 | |
| 
 | |
| 	// loop over files, search for rice.FindBox(..) calls
 | |
| 	for _, filename := range filenames {
 | |
| 		// find full filepath
 | |
| 		fullpath := filepath.Join(pkg.Dir, filename)
 | |
| 		if strings.HasSuffix(filename, "rice-box.go") {
 | |
| 			// Ignore *.rice-box.go files
 | |
| 			verbosef("skipping file %q\n", fullpath)
 | |
| 			continue
 | |
| 		}
 | |
| 		verbosef("scanning file %q\n", fullpath)
 | |
| 
 | |
| 		fset := token.NewFileSet()
 | |
| 		f, err := parser.ParseFile(fset, fullpath, nil, 0)
 | |
| 		if err != nil {
 | |
| 			fmt.Println(err)
 | |
| 			os.Exit(1)
 | |
| 		}
 | |
| 
 | |
| 		var riceIsImported bool
 | |
| 		ricePkgName := "rice"
 | |
| 		for _, imp := range f.Imports {
 | |
| 			if strings.HasSuffix(imp.Path.Value, "go.rice\"") {
 | |
| 				if imp.Name != nil {
 | |
| 					ricePkgName = imp.Name.Name
 | |
| 				}
 | |
| 				riceIsImported = true
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 		if !riceIsImported {
 | |
| 			// Rice wasn't imported, so we won't find a box.
 | |
| 			continue
 | |
| 		}
 | |
| 		if ricePkgName == "_" {
 | |
| 			// Rice pkg is unnamed, so we won't find a box.
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		// Inspect AST, looking for calls to (Must)?FindBox.
 | |
| 		// First parameter of the func must be a basic literal.
 | |
| 		// Identifiers won't be resolved.
 | |
| 		var nextIdentIsBoxFunc bool
 | |
| 		var nextBasicLitParamIsBoxName bool
 | |
| 		var boxCall token.Pos
 | |
| 		var variableToRemember string
 | |
| 		var validVariablesForBoxes map[string]bool = make(map[string]bool)
 | |
| 
 | |
| 		ast.Inspect(f, func(node ast.Node) bool {
 | |
| 			if node == nil {
 | |
| 				return false
 | |
| 			}
 | |
| 			switch x := node.(type) {
 | |
| 			// this case fixes the var := func() style assignments, not assignments to vars declared separately from the assignment.
 | |
| 			case *ast.AssignStmt:
 | |
| 				var assign = node.(*ast.AssignStmt)
 | |
| 				name, found := assign.Lhs[0].(*ast.Ident)
 | |
| 				if found {
 | |
| 					variableToRemember = name.Name
 | |
| 					composite, first := assign.Rhs[0].(*ast.CompositeLit)
 | |
| 					if first {
 | |
| 						riceSelector, second := composite.Type.(*ast.SelectorExpr)
 | |
| 
 | |
| 						if second {
 | |
| 							callCorrect := riceSelector.Sel.Name == "Config"
 | |
| 							packageName, third := riceSelector.X.(*ast.Ident)
 | |
| 
 | |
| 							if third && callCorrect && packageName.Name == ricePkgName {
 | |
| 								validVariablesForBoxes[name.Name] = true
 | |
| 								verbosef("\tfound variable, saving to scan for boxes: %q\n", name.Name)
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			case *ast.Ident:
 | |
| 				if nextIdentIsBoxFunc || ricePkgName == "." {
 | |
| 					nextIdentIsBoxFunc = false
 | |
| 					if x.Name == "FindBox" || x.Name == "MustFindBox" {
 | |
| 						nextBasicLitParamIsBoxName = true
 | |
| 						boxCall = x.Pos()
 | |
| 					}
 | |
| 				} else {
 | |
| 					if x.Name == ricePkgName || validVariablesForBoxes[x.Name] {
 | |
| 						nextIdentIsBoxFunc = true
 | |
| 					}
 | |
| 				}
 | |
| 			case *ast.BasicLit:
 | |
| 				if nextBasicLitParamIsBoxName {
 | |
| 					if x.Kind == token.STRING {
 | |
| 						nextBasicLitParamIsBoxName = false
 | |
| 						// trim "" or ``
 | |
| 						name := x.Value[1 : len(x.Value)-1]
 | |
| 						boxMap[name] = true
 | |
| 						verbosef("\tfound box %q\n", name)
 | |
| 					} else {
 | |
| 						badArgument(fset, boxCall)
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 			default:
 | |
| 				if nextIdentIsBoxFunc {
 | |
| 					nextIdentIsBoxFunc = false
 | |
| 				}
 | |
| 				if nextBasicLitParamIsBoxName {
 | |
| 					badArgument(fset, boxCall)
 | |
| 				}
 | |
| 			}
 | |
| 			return true
 | |
| 		})
 | |
| 	}
 | |
| 
 | |
| 	return boxMap
 | |
| }
 | 
