golang.org/x/tools/gopls@v0.15.3/internal/golang/completion/postfix_snippets.go (about) 1 // Copyright 2020 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 "context" 9 "fmt" 10 "go/ast" 11 "go/token" 12 "go/types" 13 "log" 14 "reflect" 15 "strings" 16 "sync" 17 "text/template" 18 19 "golang.org/x/tools/gopls/internal/cache/metadata" 20 "golang.org/x/tools/gopls/internal/golang" 21 "golang.org/x/tools/gopls/internal/golang/completion/snippet" 22 "golang.org/x/tools/gopls/internal/protocol" 23 "golang.org/x/tools/gopls/internal/util/safetoken" 24 "golang.org/x/tools/internal/event" 25 "golang.org/x/tools/internal/imports" 26 ) 27 28 // Postfix snippets are artificial methods that allow the user to 29 // compose common operations in an "argument oriented" fashion. For 30 // example, instead of "sort.Slice(someSlice, ...)" a user can expand 31 // "someSlice.sort!". 32 33 // postfixTmpl represents a postfix snippet completion candidate. 34 type postfixTmpl struct { 35 // label is the completion candidate's label presented to the user. 36 label string 37 38 // details is passed along to the client as the candidate's details. 39 details string 40 41 // body is the template text. See postfixTmplArgs for details on the 42 // facilities available to the template. 43 body string 44 45 tmpl *template.Template 46 } 47 48 // postfixTmplArgs are the template execution arguments available to 49 // the postfix snippet templates. 50 type postfixTmplArgs struct { 51 // StmtOK is true if it is valid to replace the selector with a 52 // statement. For example: 53 // 54 // func foo() { 55 // bar.sort! // statement okay 56 // 57 // someMethod(bar.sort!) // statement not okay 58 // } 59 StmtOK bool 60 61 // X is the textual SelectorExpr.X. For example, when completing 62 // "foo.bar.print!", "X" is "foo.bar". 63 X string 64 65 // Obj is the types.Object of SelectorExpr.X, if any. 66 Obj types.Object 67 68 // Type is the type of "foo.bar" in "foo.bar.print!". 69 Type types.Type 70 71 // FuncResult are results of the enclosed function 72 FuncResults []*types.Var 73 74 sel *ast.SelectorExpr 75 scope *types.Scope 76 snip snippet.Builder 77 importIfNeeded func(pkgPath string, scope *types.Scope) (name string, edits []protocol.TextEdit, err error) 78 edits []protocol.TextEdit 79 qf types.Qualifier 80 varNames map[string]bool 81 placeholders bool 82 currentTabStop int 83 } 84 85 var postfixTmpls = []postfixTmpl{{ 86 label: "sort", 87 details: "sort.Slice()", 88 body: `{{if and (eq .Kind "slice") .StmtOK -}} 89 {{.Import "sort"}}.Slice({{.X}}, func({{.VarName nil "i"}}, {{.VarName nil "j"}} int) bool { 90 {{.Cursor}} 91 }) 92 {{- end}}`, 93 }, { 94 label: "last", 95 details: "s[len(s)-1]", 96 body: `{{if and (eq .Kind "slice") .Obj -}} 97 {{.X}}[len({{.X}})-1] 98 {{- end}}`, 99 }, { 100 label: "reverse", 101 details: "reverse slice", 102 body: `{{if and (eq .Kind "slice") .StmtOK -}} 103 {{$i := .VarName nil "i"}}{{$j := .VarName nil "j" -}} 104 for {{$i}}, {{$j}} := 0, len({{.X}})-1; {{$i}} < {{$j}}; {{$i}}, {{$j}} = {{$i}}+1, {{$j}}-1 { 105 {{.X}}[{{$i}}], {{.X}}[{{$j}}] = {{.X}}[{{$j}}], {{.X}}[{{$i}}] 106 } 107 {{end}}`, 108 }, { 109 label: "range", 110 details: "range over slice", 111 body: `{{if and (eq .Kind "slice") .StmtOK -}} 112 for {{.VarName nil "i" | .Placeholder }}, {{.VarName .ElemType "v" | .Placeholder}} := range {{.X}} { 113 {{.Cursor}} 114 } 115 {{- end}}`, 116 }, { 117 label: "for", 118 details: "range over slice by index", 119 body: `{{if and (eq .Kind "slice") .StmtOK -}} 120 for {{ .VarName nil "i" | .Placeholder }} := range {{.X}} { 121 {{.Cursor}} 122 } 123 {{- end}}`, 124 }, { 125 label: "forr", 126 details: "range over slice by index and value", 127 body: `{{if and (eq .Kind "slice") .StmtOK -}} 128 for {{.VarName nil "i" | .Placeholder }}, {{.VarName .ElemType "v" | .Placeholder }} := range {{.X}} { 129 {{.Cursor}} 130 } 131 {{- end}}`, 132 }, { 133 label: "append", 134 details: "append and re-assign slice", 135 body: `{{if and (eq .Kind "slice") .StmtOK .Obj -}} 136 {{.X}} = append({{.X}}, {{.Cursor}}) 137 {{- end}}`, 138 }, { 139 label: "append", 140 details: "append to slice", 141 body: `{{if and (eq .Kind "slice") (not .StmtOK) -}} 142 append({{.X}}, {{.Cursor}}) 143 {{- end}}`, 144 }, { 145 label: "copy", 146 details: "duplicate slice", 147 body: `{{if and (eq .Kind "slice") .StmtOK .Obj -}} 148 {{$v := (.VarName nil (printf "%sCopy" .X))}}{{$v}} := make([]{{.TypeName .ElemType}}, len({{.X}})) 149 copy({{$v}}, {{.X}}) 150 {{end}}`, 151 }, { 152 label: "range", 153 details: "range over map", 154 body: `{{if and (eq .Kind "map") .StmtOK -}} 155 for {{.VarName .KeyType "k" | .Placeholder}}, {{.VarName .ElemType "v" | .Placeholder}} := range {{.X}} { 156 {{.Cursor}} 157 } 158 {{- end}}`, 159 }, { 160 label: "for", 161 details: "range over map by key", 162 body: `{{if and (eq .Kind "map") .StmtOK -}} 163 for {{.VarName .KeyType "k" | .Placeholder}} := range {{.X}} { 164 {{.Cursor}} 165 } 166 {{- end}}`, 167 }, { 168 label: "forr", 169 details: "range over map by key and value", 170 body: `{{if and (eq .Kind "map") .StmtOK -}} 171 for {{.VarName .KeyType "k" | .Placeholder}}, {{.VarName .ElemType "v" | .Placeholder}} := range {{.X}} { 172 {{.Cursor}} 173 } 174 {{- end}}`, 175 }, { 176 label: "clear", 177 details: "clear map contents", 178 body: `{{if and (eq .Kind "map") .StmtOK -}} 179 {{$k := (.VarName .KeyType "k")}}for {{$k}} := range {{.X}} { 180 delete({{.X}}, {{$k}}) 181 } 182 {{end}}`, 183 }, { 184 label: "keys", 185 details: "create slice of keys", 186 body: `{{if and (eq .Kind "map") .StmtOK -}} 187 {{$keysVar := (.VarName nil "keys")}}{{$keysVar}} := make([]{{.TypeName .KeyType}}, 0, len({{.X}})) 188 {{$k := (.VarName .KeyType "k")}}for {{$k}} := range {{.X}} { 189 {{$keysVar}} = append({{$keysVar}}, {{$k}}) 190 } 191 {{end}}`, 192 }, { 193 label: "range", 194 details: "range over channel", 195 body: `{{if and (eq .Kind "chan") .StmtOK -}} 196 for {{.VarName .ElemType "e" | .Placeholder}} := range {{.X}} { 197 {{.Cursor}} 198 } 199 {{- end}}`, 200 }, { 201 label: "for", 202 details: "range over channel", 203 body: `{{if and (eq .Kind "chan") .StmtOK -}} 204 for {{.VarName .ElemType "e" | .Placeholder}} := range {{.X}} { 205 {{.Cursor}} 206 } 207 {{- end}}`, 208 }, { 209 label: "var", 210 details: "assign to variables", 211 body: `{{if and (eq .Kind "tuple") .StmtOK -}} 212 {{$a := .}}{{range $i, $v := .Tuple}}{{if $i}}, {{end}}{{$a.VarName $v.Type $v.Name | $a.Placeholder }}{{end}} := {{.X}} 213 {{- end}}`, 214 }, { 215 label: "var", 216 details: "assign to variable", 217 body: `{{if and (ne .Kind "tuple") .StmtOK -}} 218 {{.VarName .Type "" | .Placeholder }} := {{.X}} 219 {{- end}}`, 220 }, { 221 label: "print", 222 details: "print to stdout", 223 body: `{{if and (ne .Kind "tuple") .StmtOK -}} 224 {{.Import "fmt"}}.Printf("{{.EscapeQuotes .X}}: %v\n", {{.X}}) 225 {{- end}}`, 226 }, { 227 label: "print", 228 details: "print to stdout", 229 body: `{{if and (eq .Kind "tuple") .StmtOK -}} 230 {{.Import "fmt"}}.Println({{.X}}) 231 {{- end}}`, 232 }, { 233 label: "split", 234 details: "split string", 235 body: `{{if (eq (.TypeName .Type) "string") -}} 236 {{.Import "strings"}}.Split({{.X}}, "{{.Cursor}}") 237 {{- end}}`, 238 }, { 239 label: "join", 240 details: "join string slice", 241 body: `{{if and (eq .Kind "slice") (eq (.TypeName .ElemType) "string") -}} 242 {{.Import "strings"}}.Join({{.X}}, "{{.Cursor}}") 243 {{- end}}`, 244 }, { 245 label: "ifnotnil", 246 details: "if expr != nil", 247 body: `{{if and (or (eq .Kind "pointer") (eq .Kind "chan") (eq .Kind "signature") (eq .Kind "interface") (eq .Kind "map") (eq .Kind "slice")) .StmtOK -}} 248 if {{.X}} != nil { 249 {{.Cursor}} 250 } 251 {{- end}}`, 252 }, { 253 label: "len", 254 details: "len(s)", 255 body: `{{if (eq .Kind "slice" "map" "array" "chan") -}} 256 len({{.X}}) 257 {{- end}}`, 258 }, { 259 label: "iferr", 260 details: "check error and return", 261 body: `{{if and .StmtOK (eq (.TypeName .Type) "error") -}} 262 {{- $errName := (or (and .IsIdent .X) "err") -}} 263 if {{if not .IsIdent}}err := {{.X}}; {{end}}{{$errName}} != nil { 264 return {{$a := .}}{{range $i, $v := .FuncResults}} 265 {{- if $i}}, {{end -}} 266 {{- if eq ($a.TypeName $v.Type) "error" -}} 267 {{$a.Placeholder $errName}} 268 {{- else -}} 269 {{$a.Zero $v.Type}} 270 {{- end -}} 271 {{end}} 272 } 273 {{end}}`, 274 }, { 275 label: "iferr", 276 details: "check error and return", 277 body: `{{if and .StmtOK (eq .Kind "tuple") (len .Tuple) (eq (.TypeName .TupleLast.Type) "error") -}} 278 {{- $a := . -}} 279 if {{range $i, $v := .Tuple}}{{if $i}}, {{end}}{{if and (eq ($a.TypeName $v.Type) "error") (eq (inc $i) (len $a.Tuple))}}err{{else}}_{{end}}{{end}} := {{.X -}} 280 ; err != nil { 281 return {{range $i, $v := .FuncResults}} 282 {{- if $i}}, {{end -}} 283 {{- if eq ($a.TypeName $v.Type) "error" -}} 284 {{$a.Placeholder "err"}} 285 {{- else -}} 286 {{$a.Zero $v.Type}} 287 {{- end -}} 288 {{end}} 289 } 290 {{end}}`, 291 }, { 292 // variferr snippets use nested placeholders, as described in 293 // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#snippet_syntax, 294 // so that users can wrap the returned error without modifying the error 295 // variable name. 296 label: "variferr", 297 details: "assign variables and check error", 298 body: `{{if and .StmtOK (eq .Kind "tuple") (len .Tuple) (eq (.TypeName .TupleLast.Type) "error") -}} 299 {{- $a := . -}} 300 {{- $errName := "err" -}} 301 {{- range $i, $v := .Tuple -}} 302 {{- if $i}}, {{end -}} 303 {{- if and (eq ($a.TypeName $v.Type) "error") (eq (inc $i) (len $a.Tuple)) -}} 304 {{$errName | $a.SpecifiedPlaceholder (len $a.Tuple)}} 305 {{- else -}} 306 {{$a.VarName $v.Type $v.Name | $a.Placeholder}} 307 {{- end -}} 308 {{- end}} := {{.X}} 309 if {{$errName | $a.SpecifiedPlaceholder (len $a.Tuple)}} != nil { 310 return {{range $i, $v := .FuncResults}} 311 {{- if $i}}, {{end -}} 312 {{- if eq ($a.TypeName $v.Type) "error" -}} 313 {{$errName | $a.SpecifiedPlaceholder (len $a.Tuple) | 314 $a.SpecifiedPlaceholder (inc (len $a.Tuple))}} 315 {{- else -}} 316 {{$a.Zero $v.Type}} 317 {{- end -}} 318 {{end}} 319 } 320 {{end}}`, 321 }, { 322 label: "variferr", 323 details: "assign variables and check error", 324 body: `{{if and .StmtOK (eq (.TypeName .Type) "error") -}} 325 {{- $a := . -}} 326 {{- $errName := .VarName nil "err" -}} 327 {{$errName | $a.SpecifiedPlaceholder 1}} := {{.X}} 328 if {{$errName | $a.SpecifiedPlaceholder 1}} != nil { 329 return {{range $i, $v := .FuncResults}} 330 {{- if $i}}, {{end -}} 331 {{- if eq ($a.TypeName $v.Type) "error" -}} 332 {{$errName | $a.SpecifiedPlaceholder 1 | $a.SpecifiedPlaceholder 2}} 333 {{- else -}} 334 {{$a.Zero $v.Type}} 335 {{- end -}} 336 {{end}} 337 } 338 {{end}}`, 339 }} 340 341 // Cursor indicates where the client's cursor should end up after the 342 // snippet is done. 343 func (a *postfixTmplArgs) Cursor() string { 344 return "$0" 345 } 346 347 // Placeholder indicate a tab stop with the placeholder string, the order 348 // of tab stops is the same as the order of invocation 349 func (a *postfixTmplArgs) Placeholder(placeholder string) string { 350 if !a.placeholders { 351 placeholder = "" 352 } 353 return fmt.Sprintf("${%d:%s}", a.nextTabStop(), placeholder) 354 } 355 356 // nextTabStop returns the next tab stop index for a new placeholder. 357 func (a *postfixTmplArgs) nextTabStop() int { 358 // Tab stops start from 1, so increment before returning. 359 a.currentTabStop++ 360 return a.currentTabStop 361 } 362 363 // SpecifiedPlaceholder indicate a specified tab stop with the placeholder string. 364 // Sometimes the same tab stop appears in multiple places and their numbers 365 // need to be specified. e.g. variferr 366 func (a *postfixTmplArgs) SpecifiedPlaceholder(tabStop int, placeholder string) string { 367 if !a.placeholders { 368 placeholder = "" 369 } 370 return fmt.Sprintf("${%d:%s}", tabStop, placeholder) 371 } 372 373 // Import makes sure the package corresponding to path is imported, 374 // returning the identifier to use to refer to the package. 375 func (a *postfixTmplArgs) Import(path string) (string, error) { 376 name, edits, err := a.importIfNeeded(path, a.scope) 377 if err != nil { 378 return "", fmt.Errorf("couldn't import %q: %w", path, err) 379 } 380 a.edits = append(a.edits, edits...) 381 382 return name, nil 383 } 384 385 func (a *postfixTmplArgs) EscapeQuotes(v string) string { 386 return strings.ReplaceAll(v, `"`, `\\"`) 387 } 388 389 // ElemType returns the Elem() type of xType, if applicable. 390 func (a *postfixTmplArgs) ElemType() types.Type { 391 if e, _ := a.Type.(interface{ Elem() types.Type }); e != nil { 392 return e.Elem() 393 } 394 return nil 395 } 396 397 // Kind returns the underlying kind of type, e.g. "slice", "struct", 398 // etc. 399 func (a *postfixTmplArgs) Kind() string { 400 t := reflect.TypeOf(a.Type.Underlying()) 401 return strings.ToLower(strings.TrimPrefix(t.String(), "*types.")) 402 } 403 404 // KeyType returns the type of X's key. KeyType panics if X is not a 405 // map. 406 func (a *postfixTmplArgs) KeyType() types.Type { 407 return a.Type.Underlying().(*types.Map).Key() 408 } 409 410 // Tuple returns the tuple result vars if the type of X is tuple. 411 func (a *postfixTmplArgs) Tuple() []*types.Var { 412 tuple, _ := a.Type.(*types.Tuple) 413 if tuple == nil { 414 return nil 415 } 416 417 typs := make([]*types.Var, 0, tuple.Len()) 418 for i := 0; i < tuple.Len(); i++ { 419 typs = append(typs, tuple.At(i)) 420 } 421 return typs 422 } 423 424 // TupleLast returns the last tuple result vars if the type of X is tuple. 425 func (a *postfixTmplArgs) TupleLast() *types.Var { 426 tuple, _ := a.Type.(*types.Tuple) 427 if tuple == nil { 428 return nil 429 } 430 if tuple.Len() == 0 { 431 return nil 432 } 433 return tuple.At(tuple.Len() - 1) 434 } 435 436 // TypeName returns the textual representation of type t. 437 func (a *postfixTmplArgs) TypeName(t types.Type) (string, error) { 438 if t == nil || t == types.Typ[types.Invalid] { 439 return "", fmt.Errorf("invalid type: %v", t) 440 } 441 return types.TypeString(t, a.qf), nil 442 } 443 444 // Zero return the zero value representation of type t 445 func (a *postfixTmplArgs) Zero(t types.Type) string { 446 return formatZeroValue(t, a.qf) 447 } 448 449 func (a *postfixTmplArgs) IsIdent() bool { 450 _, ok := a.sel.X.(*ast.Ident) 451 return ok 452 } 453 454 // VarName returns a suitable variable name for the type t. If t 455 // implements the error interface, "err" is used. If t is not a named 456 // type then nonNamedDefault is used. Otherwise a name is made by 457 // abbreviating the type name. If the resultant name is already in 458 // scope, an integer is appended to make a unique name. 459 func (a *postfixTmplArgs) VarName(t types.Type, nonNamedDefault string) string { 460 if t == nil { 461 t = types.Typ[types.Invalid] 462 } 463 464 var name string 465 // go/types predicates are undefined on types.Typ[types.Invalid]. 466 if !types.Identical(t, types.Typ[types.Invalid]) && types.Implements(t, errorIntf) { 467 name = "err" 468 } else if _, isNamed := golang.Deref(t).(*types.Named); !isNamed { 469 name = nonNamedDefault 470 } 471 472 if name == "" { 473 name = types.TypeString(t, func(p *types.Package) string { 474 return "" 475 }) 476 name = abbreviateTypeName(name) 477 } 478 479 if dot := strings.LastIndex(name, "."); dot > -1 { 480 name = name[dot+1:] 481 } 482 483 uniqueName := name 484 for i := 2; ; i++ { 485 if s, _ := a.scope.LookupParent(uniqueName, token.NoPos); s == nil && !a.varNames[uniqueName] { 486 break 487 } 488 uniqueName = fmt.Sprintf("%s%d", name, i) 489 } 490 491 a.varNames[uniqueName] = true 492 493 return uniqueName 494 } 495 496 func (c *completer) addPostfixSnippetCandidates(ctx context.Context, sel *ast.SelectorExpr) { 497 if !c.opts.postfix { 498 return 499 } 500 501 initPostfixRules() 502 503 if sel == nil || sel.Sel == nil { 504 return 505 } 506 507 selType := c.pkg.GetTypesInfo().TypeOf(sel.X) 508 if selType == nil { 509 return 510 } 511 512 // Skip empty tuples since there is no value to operate on. 513 if tuple, ok := selType.Underlying().(*types.Tuple); ok && tuple == nil { 514 return 515 } 516 517 tokFile := c.pkg.FileSet().File(c.pos) 518 519 // Only replace sel with a statement if sel is already a statement. 520 var stmtOK bool 521 for i, n := range c.path { 522 if n == sel && i < len(c.path)-1 { 523 switch p := c.path[i+1].(type) { 524 case *ast.ExprStmt: 525 stmtOK = true 526 case *ast.AssignStmt: 527 // In cases like: 528 // 529 // foo.<> 530 // bar = 123 531 // 532 // detect that "foo." makes up the entire statement since the 533 // apparent selector spans lines. 534 stmtOK = safetoken.Line(tokFile, c.pos) < safetoken.Line(tokFile, p.TokPos) 535 } 536 break 537 } 538 } 539 540 var funcResults []*types.Var 541 if c.enclosingFunc != nil { 542 results := c.enclosingFunc.sig.Results() 543 if results != nil { 544 funcResults = make([]*types.Var, results.Len()) 545 for i := 0; i < results.Len(); i++ { 546 funcResults[i] = results.At(i) 547 } 548 } 549 } 550 551 scope := c.pkg.GetTypes().Scope().Innermost(c.pos) 552 if scope == nil { 553 return 554 } 555 556 // afterDot is the position after selector dot, e.g. "|" in 557 // "foo.|print". 558 afterDot := sel.Sel.Pos() 559 560 // We must detect dangling selectors such as: 561 // 562 // foo.<> 563 // bar 564 // 565 // and adjust afterDot so that we don't mistakenly delete the 566 // newline thinking "bar" is part of our selector. 567 if startLine := safetoken.Line(tokFile, sel.Pos()); startLine != safetoken.Line(tokFile, afterDot) { 568 if safetoken.Line(tokFile, c.pos) != startLine { 569 return 570 } 571 afterDot = c.pos 572 } 573 574 for _, rule := range postfixTmpls { 575 // When completing foo.print<>, "print" is naturally overwritten, 576 // but we need to also remove "foo." so the snippet has a clean 577 // slate. 578 edits, err := c.editText(sel.Pos(), afterDot, "") 579 if err != nil { 580 event.Error(ctx, "error calculating postfix edits", err) 581 return 582 } 583 584 tmplArgs := postfixTmplArgs{ 585 X: golang.FormatNode(c.pkg.FileSet(), sel.X), 586 StmtOK: stmtOK, 587 Obj: exprObj(c.pkg.GetTypesInfo(), sel.X), 588 Type: selType, 589 FuncResults: funcResults, 590 sel: sel, 591 qf: c.qf, 592 importIfNeeded: c.importIfNeeded, 593 scope: scope, 594 varNames: make(map[string]bool), 595 placeholders: c.opts.placeholders, 596 } 597 598 // Feed the template straight into the snippet builder. This 599 // allows templates to build snippets as they are executed. 600 err = rule.tmpl.Execute(&tmplArgs.snip, &tmplArgs) 601 if err != nil { 602 event.Error(ctx, "error executing postfix template", err) 603 continue 604 } 605 606 if strings.TrimSpace(tmplArgs.snip.String()) == "" { 607 continue 608 } 609 610 score := c.matcher.Score(rule.label) 611 if score <= 0 { 612 continue 613 } 614 615 c.items = append(c.items, CompletionItem{ 616 Label: rule.label + "!", 617 Detail: rule.details, 618 Score: float64(score) * 0.01, 619 Kind: protocol.SnippetCompletion, 620 snippet: &tmplArgs.snip, 621 AdditionalTextEdits: append(edits, tmplArgs.edits...), 622 }) 623 } 624 } 625 626 var postfixRulesOnce sync.Once 627 628 func initPostfixRules() { 629 postfixRulesOnce.Do(func() { 630 var idx int 631 for _, rule := range postfixTmpls { 632 var err error 633 rule.tmpl, err = template.New("postfix_snippet").Funcs(template.FuncMap{ 634 "inc": inc, 635 }).Parse(rule.body) 636 if err != nil { 637 log.Panicf("error parsing postfix snippet template: %v", err) 638 } 639 postfixTmpls[idx] = rule 640 idx++ 641 } 642 postfixTmpls = postfixTmpls[:idx] 643 }) 644 } 645 646 func inc(i int) int { 647 return i + 1 648 } 649 650 // importIfNeeded returns the package identifier and any necessary 651 // edits to import package pkgPath. 652 func (c *completer) importIfNeeded(pkgPath string, scope *types.Scope) (string, []protocol.TextEdit, error) { 653 defaultName := imports.ImportPathToAssumedName(pkgPath) 654 655 // Check if file already imports pkgPath. 656 for _, s := range c.file.Imports { 657 // TODO(adonovan): what if pkgPath has a vendor/ suffix? 658 // This may be the cause of go.dev/issue/56291. 659 if string(metadata.UnquoteImportPath(s)) == pkgPath { 660 if s.Name == nil { 661 return defaultName, nil, nil 662 } 663 if s.Name.Name != "_" { 664 return s.Name.Name, nil, nil 665 } 666 } 667 } 668 669 // Give up if the package's name is already in use by another object. 670 if _, obj := scope.LookupParent(defaultName, token.NoPos); obj != nil { 671 return "", nil, fmt.Errorf("import name %q of %q already in use", defaultName, pkgPath) 672 } 673 674 edits, err := c.importEdits(&importInfo{ 675 importPath: pkgPath, 676 }) 677 if err != nil { 678 return "", nil, err 679 } 680 681 return defaultName, edits, nil 682 }