golang.org/x/tools/gopls@v0.15.3/internal/golang/completion/fuzz.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package completion 6 7 import ( 8 "fmt" 9 "go/ast" 10 "go/types" 11 "strings" 12 13 "golang.org/x/tools/gopls/internal/protocol" 14 ) 15 16 // golang/go#51089 17 // *testing.F deserves special treatment as member use is constrained: 18 // The arguments to f.Fuzz are determined by the arguments to a previous f.Add 19 // Inside f.Fuzz only f.Failed and f.Name are allowed. 20 // PJW: are there other packages where we can deduce usage constraints? 21 22 // if we find fuzz completions, then return true, as those are the only completions to offer 23 func (c *completer) fuzz(mset *types.MethodSet, imp *importInfo, cb func(candidate)) bool { 24 // 1. inside f.Fuzz? (only f.Failed and f.Name) 25 // 2. possible completing f.Fuzz? 26 // [Ident,SelectorExpr,Callexpr,ExprStmt,BlockiStmt,FuncDecl(Fuzz...)] 27 // 3. before f.Fuzz, same (for 2., offer choice when looking at an F) 28 29 // does the path contain FuncLit as arg to f.Fuzz CallExpr? 30 inside := false 31 Loop: 32 for i, n := range c.path { 33 switch v := n.(type) { 34 case *ast.CallExpr: 35 if len(v.Args) != 1 { 36 continue Loop 37 } 38 if _, ok := v.Args[0].(*ast.FuncLit); !ok { 39 continue 40 } 41 if s, ok := v.Fun.(*ast.SelectorExpr); !ok || s.Sel.Name != "Fuzz" { 42 continue 43 } 44 if i > 2 { // avoid t.Fuzz itself in tests 45 inside = true 46 break Loop 47 } 48 } 49 } 50 if inside { 51 for i := 0; i < mset.Len(); i++ { 52 o := mset.At(i).Obj() 53 if o.Name() == "Failed" || o.Name() == "Name" { 54 cb(candidate{ 55 obj: o, 56 score: stdScore, 57 imp: imp, 58 addressable: true, 59 }) 60 } 61 } 62 return true 63 } 64 // if it could be t.Fuzz, look for the preceding t.Add 65 id, ok := c.path[0].(*ast.Ident) 66 if ok && strings.HasPrefix("Fuzz", id.Name) { 67 var add *ast.CallExpr 68 f := func(n ast.Node) bool { 69 if n == nil { 70 return true 71 } 72 call, ok := n.(*ast.CallExpr) 73 if !ok { 74 return true 75 } 76 s, ok := call.Fun.(*ast.SelectorExpr) 77 if !ok { 78 return true 79 } 80 if s.Sel.Name != "Add" { 81 return true 82 } 83 // Sel.X should be of type *testing.F 84 got := c.pkg.GetTypesInfo().Types[s.X] 85 if got.Type.String() == "*testing.F" { 86 add = call 87 } 88 return false // because we're done... 89 } 90 // look at the enclosing FuzzFoo functions 91 if len(c.path) < 2 { 92 return false 93 } 94 n := c.path[len(c.path)-2] 95 if _, ok := n.(*ast.FuncDecl); !ok { 96 // the path should start with ast.File, ast.FuncDecl, ... 97 // but it didn't, so give up 98 return false 99 } 100 ast.Inspect(n, f) 101 if add == nil { 102 // looks like f.Fuzz without a preceding f.Add. 103 // let the regular completion handle it. 104 return false 105 } 106 107 lbl := "Fuzz(func(t *testing.T" 108 for i, a := range add.Args { 109 info := c.pkg.GetTypesInfo().TypeOf(a) 110 if info == nil { 111 return false // How could this happen, but better safe than panic. 112 } 113 lbl += fmt.Sprintf(", %c %s", 'a'+i, info) 114 } 115 lbl += ")" 116 xx := CompletionItem{ 117 Label: lbl, 118 InsertText: lbl, 119 Kind: protocol.FunctionCompletion, 120 Depth: 0, 121 Score: 10, // pretty confident the user should see this 122 Documentation: "argument types from f.Add", 123 isSlice: false, 124 } 125 c.items = append(c.items, xx) 126 for i := 0; i < mset.Len(); i++ { 127 o := mset.At(i).Obj() 128 if o.Name() != "Fuzz" { 129 cb(candidate{ 130 obj: o, 131 score: stdScore, 132 imp: imp, 133 addressable: true, 134 }) 135 } 136 } 137 return true // done 138 } 139 // let the standard processing take care of it instead 140 return false 141 }