func findRouteTableAST(file *ast.File) (routeTableAST *ast.CompositeLit) { defer func() { if err := recover(); err != nil && err != ErrRouteTableASTIsFound { panic(err) } }() ast.Inspect(file, func(node ast.Node) bool { switch aType := node.(type) { case *ast.GenDecl: if aType.Tok != token.VAR { return false } ast.Inspect(aType, func(n ast.Node) bool { switch typ := n.(type) { case *ast.CompositeLit: switch t := typ.Type.(type) { case *ast.Ident: if t.Name == routeTableTypeName { routeTableAST = typ panic(ErrRouteTableASTIsFound) } } } return true }) } return true }) return routeTableAST }
func rewriteConflictingNames(fn *ast.FuncDecl) { for _, fieldList := range []*ast.FieldList{fn.Recv, fn.Type.Params, fn.Type.Results} { if fieldList == nil { continue } for _, f := range fieldList.List { for _, name := range f.Names { if name.Name == fn.Name.Name { oldName := name.Name newName := createConflictFreeNameCheckIdents(name.Name, fn) rewriteFn := func(node ast.Node) bool { if ident, ok := node.(*ast.Ident); ok && ident.Name == oldName { ident.Name = newName } return true } // Instead of walking all of fn, walk fn.Recv, fn.Type, and fn.Body. // If we walked all of fn we would rewrite the name of the function itself // in addition to the parameter we are rewriting. if fn.Recv != nil && fn.Recv.List != nil { ast.Inspect(fn.Recv, rewriteFn) } if fn.Type != nil { ast.Inspect(fn.Type, rewriteFn) } if fn.Body != nil { ast.Inspect(fn.Body, rewriteFn) } return // at most one parameter can share a name with the function } } } } }
// GetPublicFunctions lists all public functions (not methods) from a golang source file. func GetPublicFunctions(filePath string) ([]string, error) { var functionNames []string // Create the AST by parsing src. fset := token.NewFileSet() // positions are relative to fset f, err := parser.ParseFile(fset, filePath, nil, 0) if err != nil { return nil, fmt.Errorf("failed parse file to list functions: %v", err) } // Inspect the AST and print all identifiers and literals. ast.Inspect(f, func(n ast.Node) bool { var s string switch x := n.(type) { case *ast.FuncDecl: s = x.Name.Name // It's a function (not method), and is public, record it. if x.Recv == nil && isPublic(s) { functionNames = append(functionNames, s) } } return true }) return functionNames, nil }
// Build constructs a control flow graph, // filtered to include only interesting nodes. func Build(fset *token.FileSet, body *ast.BlockStmt, interesting func(ast.Node) bool) *Graph { start := &ast.Ident{Name: "_cfg_start_"} end := &ast.Ident{Name: "_cfg_end_"} b := &builder{ interesting: interesting, followCache: make(map[ast.Node][]ast.Node), end: end, need: make(map[ast.Node]bool), trimmed: make(map[ast.Node]bool), followed: make(map[ast.Node]bool), brkLabel: make(map[string][]ast.Node), contLabel: make(map[string][]ast.Node), gotoLabel: make(map[string]ast.Node), isGotoTarget: make(map[string]bool), stmtLabel: make(map[ast.Stmt]string), } ast.Inspect(body, b.scanGoto) b.followCache[start] = b.trimList(b.follow(body, []ast.Node{end})) return &Graph{ FileSet: fset, Start: start, End: end, Follow: b.followCache, } }
// Scan app source code for calls to X.Y(), where X is of type *Validation. // // Recognize these scenarios: // - "Y" = "Validation" and is a member of the receiver. // (The common case for inline validation) // - "X" is passed in to the func as a parameter. // (For structs implementing Validated) // // The line number to which a validation call is attributed is that of the // surrounding ExprStmt. This is so that it matches what runtime.Callers() // reports. // // The end result is that we can set the default validation key for each call to // be the same as the local variable. func getValidationKeys(fset *token.FileSet, funcDecl *ast.FuncDecl, imports map[string]string) map[int]string { var ( lineKeys = make(map[int]string) // Check the func parameters and the receiver's members for the *revel.Validation type. validationParam = getValidationParameter(funcDecl, imports) ) ast.Inspect(funcDecl.Body, func(node ast.Node) bool { // e.g. c.Validation.Required(arg) or v.Required(arg) callExpr, ok := node.(*ast.CallExpr) if !ok { return true } // e.g. c.Validation.Required or v.Required funcSelector, ok := callExpr.Fun.(*ast.SelectorExpr) if !ok { return true } switch x := funcSelector.X.(type) { case *ast.SelectorExpr: // e.g. c.Validation if x.Sel.Name != "Validation" { return true } case *ast.Ident: // e.g. v if validationParam == nil || x.Obj != validationParam { return true } default: return true } if len(callExpr.Args) == 0 { return true } // If the argument is a binary expression, take the first expression. // (e.g. c.Validation.Required(myName != "")) arg := callExpr.Args[0] if binExpr, ok := arg.(*ast.BinaryExpr); ok { arg = binExpr.X } // If it's a literal, skip it. if _, ok = arg.(*ast.BasicLit); ok { return true } if typeExpr := NewTypeExpr("", arg); typeExpr.Valid { lineKeys[fset.Position(callExpr.End()).Line] = typeExpr.TypeName("") } return true }) return lineKeys }
func init() { act(Action{ Path: "/imports", Doc: "", Func: func(r Request) (data, error) { res := ImportsResult{} a := ImportsArgs{ Toggle: []ImportDeclArg{}, TabWidth: 8, TabIndent: true, } if err := r.Decode(&a); err != nil { return res, err } fset, af, err := parseAstFile(a.Fn, a.Src, parser.ImportsOnly|parser.ParseComments) if err == nil { ast.Inspect(af, func(n ast.Node) bool { if n != nil { tp := fset.Position(n.End()) if tp.Line > res.LineRef { res.LineRef = tp.Line } } return true }) af = imp(fset, af, a.Toggle) res.Src, err = printSrc(fset, af, a.TabIndent, a.TabWidth) } return res, err }, }) }
func StructFuncs(f *ast.File) map[string]funcs { structFuncs := map[string]funcs{} ast.Inspect(f, func(n ast.Node) bool { f, ok := n.(*ast.FuncDecl) if !ok { return true } recv, ok := findRecv(f.Recv) if !ok { return true } fn := funcType{ Recv: recv, Name: f.Name.Name, Comments: findComments(f.Doc), } structFuncs[recv] = append(structFuncs[recv], fn) return false }) return structFuncs }
func (sms *ShowMissingStrings) extractString(f *ast.File, fset *token.FileSet, filename string) error { ast.Inspect(f, func(n ast.Node) bool { switch x := n.(type) { case *ast.CallExpr: switch x.Fun.(type) { case *ast.Ident: funName := x.Fun.(*ast.Ident).Name if funName == "T" || funName == "t" { if stringArg, ok := x.Args[0].(*ast.BasicLit); ok { translatedString, err := strconv.Unquote(stringArg.Value) if err != nil { panic(err.Error()) } sms.Println("Adding to translated strings:", translatedString) sms.TranslatedStrings = append(sms.TranslatedStrings, filename+": "+translatedString) } } default: //Skip! } } return true }) return nil }
// checkRangeLoop walks the body of the provided range statement, checking if // its index or value variables are used unsafely inside goroutines or deferred // function literals. func checkRangeLoop(f *File, node ast.Node) { n := node.(*ast.RangeStmt) key, _ := n.Key.(*ast.Ident) val, _ := n.Value.(*ast.Ident) if key == nil && val == nil { return } sl := n.Body.List if len(sl) == 0 { return } var last *ast.CallExpr switch s := sl[len(sl)-1].(type) { case *ast.GoStmt: last = s.Call case *ast.DeferStmt: last = s.Call default: return } lit, ok := last.Fun.(*ast.FuncLit) if !ok { return } ast.Inspect(lit.Body, func(n ast.Node) bool { id, ok := n.(*ast.Ident) if !ok || id.Obj == nil { return true } if key != nil && id.Obj == key.Obj || val != nil && id.Obj == val.Obj { f.Bad(id.Pos(), "range variable", id.Name, "captured by func literal") } return true }) }
func lintCheckFlagParse(fset *token.FileSet, af *ast.File, res []AcLintReport) []AcLintReport { reps := []AcLintReport{} foundParse := false ast.Inspect(af, func(node ast.Node) bool { switch c := node.(type) { case *ast.CallExpr: if sel, ok := c.Fun.(*ast.SelectorExpr); ok { if id, ok := sel.X.(*ast.Ident); ok && id.Name == "flag" { if sel.Sel.String() == "Parse" { foundParse = true } else if !foundParse && c != nil { tp := fset.Position(c.Pos()) if tp.IsValid() { reps = append(reps, AcLintReport{ Row: tp.Line - 1, Col: tp.Column - 1, Msg: "Cannot find corresponding call to flag.Parse()", }) } } } } } return !foundParse }) if !foundParse { res = append(res, reps...) } return res }
func (f *file) checkFileContent() { if f.isTest() { return } if f.config.PackageName { f.checkPkgName(f.ast.Name) } for _, v := range f.ast.Decls { switch decl := v.(type) { case *ast.FuncDecl: f.checkFunctionDeclare(decl) if decl.Body == nil { break } ast.Inspect(decl.Body, func(node ast.Node) bool { switch decl2 := node.(type) { case *ast.GenDecl: f.checkGenDecl(decl2, false) case *ast.FuncDecl: f.checkFunctionDeclare(decl2) case *ast.AssignStmt: f.checkAssign(decl2) case *ast.StructType: f.checkStruct(decl2) } return true }) case *ast.GenDecl: f.checkGenDecl(decl, true) } } }
func (f *file) checkValueName(decl *ast.GenDecl, kind string, top bool) { for _, spec := range decl.Specs { if vSpec, ok := spec.(*ast.ValueSpec); ok { for _, name := range vSpec.Names { f.checkName(name, kind, !top) } } else if tSpec, ok := spec.(*ast.TypeSpec); ok { f.checkName(tSpec.Name, kind, false) ast.Inspect(tSpec.Type, func(node ast.Node) bool { switch decl2 := node.(type) { case *ast.GenDecl: f.checkGenDecl(decl2, false) case *ast.FuncDecl: f.checkFunctionDeclare(decl2) case *ast.StructType: f.checkStruct(decl2) case *ast.InterfaceType: f.checkInterface(decl2) } return true }) } else if iSpec, ok := spec.(*ast.ImportSpec); ok && iSpec.Name != nil { f.checkName(iSpec.Name, "import", true) } } }
func enumerateAstNodesInPackage(iterator func(filename string, node ast.Node) bool) error { packageRoot, err := getPackageFolderPath() if err != nil { return err } files, err := ioutil.ReadDir(packageRoot) if err != nil { return fmt.Errorf("Error listing package root: %v", err) } fileSet := token.NewFileSet() for _, f := range files { if strings.HasSuffix(f.Name(), ".go") { contents, err := ioutil.ReadFile(filepath.Join(packageRoot, f.Name())) if err != nil { return fmt.Errorf("Error reading contents of go file in package: %v", err) } parsed, err := parser.ParseFile(fileSet, f.Name(), contents, 0) if err != nil { return fmt.Errorf("Error parsing source file %s: %v", f.Name(), err) } ast.Inspect(parsed, func(n ast.Node) bool { return iterator(f.Name(), n) }) } } return nil }
func printNode(n ast.Node) { ast.Inspect(n, func(n ast.Node) bool { fmt.Printf("%T: %v\n", n, n) return true }) }
func (fix *Fixup) inspectFile(file string) (translatedStrings []string, err error) { fset := token.NewFileSet() astFile, err := parser.ParseFile(fset, file, nil, parser.AllErrors) if err != nil { fix.Println(err) return } ast.Inspect(astFile, func(n ast.Node) bool { switch x := n.(type) { case *ast.CallExpr: switch x.Fun.(type) { case *ast.Ident: funName := x.Fun.(*ast.Ident).Name if funName == "T" || funName == "t" { if stringArg, ok := x.Args[0].(*ast.BasicLit); ok { translatedString, err := strconv.Unquote(stringArg.Value) if err != nil { panic(err.Error()) } translatedStrings = append(translatedStrings, translatedString) } } default: //Skip! } } return true }) return }
func main() { fset := token.NewFileSet() f, err := parser.ParseFile(fset, "example_test.go", nil, 0) if err != nil { fmt.Println(err) return } for _, s := range f.Imports { fmt.Println(s.Path.Value) } for _, decl := range f.Decls { ast.Inspect(decl, func(node ast.Node) bool { fn, ok := node.(*ast.FuncDecl) if !ok { return false } fnName := fn.Name.Name for _, param := range fn.Type.Params.List { typeName := param.Type.(*ast.Ident).Name for _, name := range param.Names { println(fnName, name.Name, typeName) } } return false }) } }
// This function checks that all the functions using the tests // defined in indextest, namely: // TestIndex_, TestPathOfSignerTarget_, TestFiles_ // do exist in the provided test file. func hasAllRequiredTests(name string, t *testing.T) error { tests := make(map[string]bool) for _, v := range requiredTests { tests[v] = false } if !strings.HasSuffix(name, "_test.go") || skipFromList(name) { return nil } fset := token.NewFileSet() f, err := parser.ParseFile(fset, name, nil, 0) if err != nil { t.Fatalf("%v: %v", name, err) } ast.Inspect(f, func(n ast.Node) bool { switch x := n.(type) { case *ast.FuncDecl: name := x.Name.Name for k, _ := range tests { if strings.HasPrefix(name, k) { tests[k] = true } } } return true }) for k, v := range tests { if !v { return fmt.Errorf("%v not implemented in %v", k, name) } } return nil }
// getTaggedComments walks the AST and returns types which have directive comment // returns a map of TypeSpec to directive func getTaggedComments(pkg *ast.Package, directive string) map[*ast.TypeSpec]*ast.Comment { specs := make(map[*ast.TypeSpec]*ast.Comment) ast.Inspect(pkg, func(n ast.Node) bool { g, ok := n.(*ast.GenDecl) // is it a type? // http://golang.org/pkg/go/ast/#GenDecl if !ok || g.Tok != token.TYPE { // never mind, move on return true } if g.Lparen == 0 { // not parenthesized, copy GenDecl.Doc into TypeSpec.Doc g.Specs[0].(*ast.TypeSpec).Doc = g.Doc } for _, s := range g.Specs { t := s.(*ast.TypeSpec) if c := findAnnotation(t.Doc, directive); c != nil { specs[t] = c } } // no need to keep walking, we don't care about TypeSpec's children return false }) return specs }
func processFile(filename string) []Function { var res []Function fset := token.NewFileSet() // positions are relative to fset f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments) if err != nil { panic(err) } cmap := ast.NewCommentMap(fset, f, f.Comments) ast.Inspect(f, func(n ast.Node) bool { switch x := n.(type) { case *ast.FuncDecl: fun := Function{Begin: fset.Position(x.Pos()).Line, Package: f.Name.String(), Name: x.Name.String(), End: fset.Position(x.End()).Line, Filepath: fset.Position(x.Pos()).Filename} fun.getTSpec(cmap[n]) res = append(res, fun) } return true }) return res }
// TestIncompleteSelection ensures that an incomplete selector // expression is parsed as a (blank) *ast.SelectorExpr, not a // *ast.BadExpr. func TestIncompleteSelection(t *testing.T) { for _, src := range []string{ "package p; var _ = fmt.", // at EOF "package p; var _ = fmt.\ntype X int", // not at EOF } { fset := token.NewFileSet() f, err := ParseFile(fset, "", src, 0) if err == nil { t.Errorf("ParseFile(%s) succeeded unexpectedly", src) continue } const wantErr = "expected selector or type assertion" if !strings.Contains(err.Error(), wantErr) { t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr) } var sel *ast.SelectorExpr ast.Inspect(f, func(n ast.Node) bool { if n, ok := n.(*ast.SelectorExpr); ok { sel = n } return true }) if sel == nil { t.Error("found no *ast.SelectorExpr") continue } const wantSel = "&{fmt _}" if fmt.Sprint(sel) != wantSel { t.Errorf("found selector %s, want %s", sel, wantSel) continue } } }
func AnotatedStructs(f *ast.File, anotation string) structs { structs := structs{} pkg := f.Name.Name ast.Inspect(f, func(n ast.Node) bool { g, ok := n.(*ast.GenDecl) if !ok || g.Tok != token.TYPE { return true } comments := findComments(g.Doc) if !isMarked(comments, anotation) { return true } st, ok := findStruct(g.Specs) if !ok { return true } st.Comments = comments st.Package = pkg structs = append(structs, st) return false }) return structs }
func main() { f, err := parser.ParseFile(fset, "hello.go", input, 0) if err != nil { log.Fatal(err) // parse error } conf := types.Config{Importer: importer.Default()} info := &types.Info{Types: make(map[ast.Expr]types.TypeAndValue)} if _, err := conf.Check("cmd/hello", fset, []*ast.File{f}, info); err != nil { log.Fatal(err) // type error } //!+inspect // f is a parsed, type-checked *ast.File. ast.Inspect(f, func(n ast.Node) bool { if expr, ok := n.(ast.Expr); ok { if tv, ok := info.Types[expr]; ok { fmt.Printf("%-24s\tmode: %s\n", nodeString(expr), mode(tv)) fmt.Printf("\t\t\t\ttype: %v\n", tv.Type) if tv.Value != nil { fmt.Printf("\t\t\t\tvalue: %v\n", tv.Value) } } } return true }) //!-inspect }
func (pkg *PackageInfo) TypeInfos() TypeInfos { var types TypeInfos for _, file := range pkg.Files { if file == nil { continue } ast.Inspect(file.AstFile(), func(node ast.Node) bool { decl, ok := node.(*ast.GenDecl) if !ok || decl.Tok != token.TYPE { return true } found := false for _, spec := range decl.Specs { ts, ok := spec.(*ast.TypeSpec) if !ok { continue } types = append(types, &TypeInfo{ FileInfo: file, GenDecl: decl, TypeSpec: ts, }) found = true } return !found }) } return types }
func main() { flag.Parse() importPaths := gotool.ImportPaths(flag.Args()) if len(importPaths) == 0 { return } var conf loader.Config conf.Fset = fset for _, importPath := range importPaths { conf.Import(importPath) } prog, err := conf.Load() if err != nil { log.Fatal(err) } for _, pkg := range prog.InitialPackages() { for _, file := range pkg.Files { ast.Inspect(file, func(node ast.Node) bool { if s, ok := node.(*ast.StructType); ok { malign(node.Pos(), pkg.Types[s].Type.(*types.Struct)) } return true }) } } }
func main() { outputFile := flag.String("o", "", "Output file, blank for stdout") flag.Parse() fname := flag.Arg(0) fset := token.NewFileSet() f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments) if err != nil { log.Fatal(err) } var structs []structInfo i := inspector(&structs) ast.Inspect(f, i) var output io.Writer = os.Stdout if *outputFile != "" { fd, err := os.Create(*outputFile) if err != nil { log.Fatal(err) } output = fd } headerTpl.Execute(output, map[string]string{"Package": f.Name.Name}) for _, s := range structs { fmt.Fprintf(output, "\n/*\n\n") generateDiagram(output, s) generateXdr(output, s) fmt.Fprintf(output, "*/\n") generateCode(output, s) } }
func varIdentsDeclaredWithin(nodes []ast.Node) map[string]*ast.Ident { result := make(map[string]*ast.Ident) for _, node := range nodes { ast.Inspect(node, func(node ast.Node) bool { switch typedNode := node.(type) { case *ast.ValueSpec: for _, name := range typedNode.Names { result[name.Name] = name } case *ast.AssignStmt: if typedNode.Tok.String() == ":=" { for i := range typedNode.Lhs { result[typedNode.Lhs[i].(*ast.Ident).Name] = typedNode.Lhs[i].(*ast.Ident) } } case *ast.RangeStmt: if typedNode.Tok.String() == ":=" { result[typedNode.Key.(*ast.Ident).Name] = typedNode.Key.(*ast.Ident) if typedNode.Value != nil { result[typedNode.Value.(*ast.Ident).Name] = typedNode.Value.(*ast.Ident) } } } return true }) } return result }
func PrintHugeParams(fset *token.FileSet, info *types.Info, files []*ast.File) { checkTuple := func(descr string, tuple *types.Tuple) { for i := 0; i < tuple.Len(); i++ { v := tuple.At(i) if sz := sizeof(v.Type()); sz > int64(*bytesFlag) { fmt.Printf("%s: %q %s: %s = %d bytes\n", fset.Position(v.Pos()), v.Name(), descr, v.Type(), sz) } } } checkSig := func(sig *types.Signature) { checkTuple("parameter", sig.Params()) checkTuple("result", sig.Results()) } for _, file := range files { ast.Inspect(file, func(n ast.Node) bool { switch n := n.(type) { case *ast.FuncDecl: checkSig(info.Defs[n.Name].Type().(*types.Signature)) case *ast.FuncLit: checkSig(info.Types[n.Type].Type.(*types.Signature)) } return true }) } }
func removeCommentsFrom(decls []ast.Decl) { for _, decl := range decls { ast.Inspect(decl, func(n ast.Node) bool { switch d := n.(type) { case *ast.GenDecl: d.Doc = nil for _, spec := range d.Specs { switch s := spec.(type) { case *ast.ImportSpec: s.Doc = nil s.Comment = nil case *ast.ValueSpec: s.Doc = nil s.Comment = nil case *ast.TypeSpec: s.Doc = nil s.Comment = nil } } case *ast.FuncDecl: d.Doc = nil case *ast.StructType: for _, field := range d.Fields.List { field.Doc = nil field.Comment = nil } } return true }) } }
func stripParens(x ast.Expr) ast.Expr { if px, strip := x.(*ast.ParenExpr); strip { // parentheses must not be stripped if there are any // unparenthesized composite literals starting with // a type name ast.Inspect(px.X, func(node interface{}) bool { switch x := node.(type) { case *ast.ParenExpr: // parentheses protect enclosed composite literals return false case *ast.CompositeLit: if isTypeName(x.Type) { strip = false // do not strip parentheses } return false } // in all other cases, keep inspecting return true }) if strip { return stripParens(px.X) } } return x }
func (es *extractStrings) extractString(f *ast.File, fset *token.FileSet) error { shouldProcessBasicLit := true ast.Inspect(f, func(n ast.Node) bool { switch x := n.(type) { case *ast.BasicLit: if shouldProcessBasicLit { es.processBasicLit(x, n, fset) } shouldProcessBasicLit = true case *ast.IndexExpr: _, ok := x.Index.(*ast.BasicLit) if ok { shouldProcessBasicLit = false } case *ast.KeyValueExpr: _, ok := x.Key.(*ast.BasicLit) if ok { shouldProcessBasicLit = false } } return true }) return nil }