@@ -10,7 +10,6 @@ import (
1010 "go/ast"
1111 "go/token"
1212 "go/types"
13- "strings"
1413
1514 "golang.org/x/tools/go/ast/astutil"
1615 "golang.org/x/tools/gopls/internal/lsp/protocol"
@@ -67,10 +66,27 @@ func highlightPath(path []ast.Node, file *ast.File, info *types.Info) (map[posRa
6766 result := make (map [posRange ]struct {})
6867 switch node := path [0 ].(type ) {
6968 case * ast.BasicLit :
69+ // Import path string literal?
7070 if len (path ) > 1 {
71- if _ , ok := path [1 ].(* ast.ImportSpec ); ok {
72- err := highlightImportUses (path , info , result )
73- return result , err
71+ if imp , ok := path [1 ].(* ast.ImportSpec ); ok {
72+ highlight := func (n ast.Node ) {
73+ result [posRange {start : n .Pos (), end : n .End ()}] = struct {}{}
74+ }
75+
76+ // Highlight the import itself...
77+ highlight (imp )
78+
79+ // ...and all references to it in the file.
80+ if pkgname , ok := ImportedPkgName (info , imp ); ok {
81+ ast .Inspect (file , func (n ast.Node ) bool {
82+ if id , ok := n .(* ast.Ident ); ok &&
83+ info .Uses [id ] == pkgname {
84+ highlight (id )
85+ }
86+ return true
87+ })
88+ }
89+ return result , nil
7490 }
7591 }
7692 highlightFuncControlFlow (path , result )
@@ -419,66 +435,46 @@ Outer:
419435 })
420436}
421437
422- func highlightImportUses (path []ast.Node , info * types.Info , result map [posRange ]struct {}) error {
423- basicLit , ok := path [0 ].(* ast.BasicLit )
424- if ! ok {
425- return fmt .Errorf ("highlightImportUses called with an ast.Node of type %T" , basicLit )
426- }
427- ast .Inspect (path [len (path )- 1 ], func (node ast.Node ) bool {
428- if imp , ok := node .(* ast.ImportSpec ); ok && imp .Path == basicLit {
429- result [posRange {start : node .Pos (), end : node .End ()}] = struct {}{}
430- return false
431- }
432- n , ok := node .(* ast.Ident )
433- if ! ok {
434- return true
435- }
436- obj , ok := info .ObjectOf (n ).(* types.PkgName )
437- if ! ok {
438- return true
439- }
440- if ! strings .Contains (basicLit .Value , obj .Name ()) {
441- return true
442- }
438+ func highlightIdentifier (id * ast.Ident , file * ast.File , info * types.Info , result map [posRange ]struct {}) {
439+ highlight := func (n ast.Node ) {
443440 result [posRange {start : n .Pos (), end : n .End ()}] = struct {}{}
444- return false
445- })
446- return nil
447- }
441+ }
448442
449- func highlightIdentifier (id * ast.Ident , file * ast.File , info * types.Info , result map [posRange ]struct {}) {
450- // TODO(rfindley): idObj may be nil. Note that returning early in this case
451- // causes tests to fail (because the nObj == idObj check below was succeeded
452- // for nil == nil!)
453- //
454- // Revisit this. If ObjectOf is nil, there are type errors, and it seems
455- // reasonable for identifier highlighting not to work.
456- idObj := info .ObjectOf (id )
457- pkgObj , isImported := idObj .(* types.PkgName )
458- ast .Inspect (file , func (node ast.Node ) bool {
459- if imp , ok := node .(* ast.ImportSpec ); ok && isImported {
460- highlightImport (pkgObj , imp , result )
461- }
462- n , ok := node .(* ast.Ident )
463- if ! ok {
464- return true
465- }
466- if n .Name != id .Name {
467- return false
468- }
469- if nObj := info .ObjectOf (n ); nObj == idObj {
470- result [posRange {start : n .Pos (), end : n .End ()}] = struct {}{}
443+ // obj may be nil if the Ident is undefined.
444+ // In this case, the behavior expected by tests is
445+ // to match other undefined Idents of the same name.
446+ obj := info .ObjectOf (id )
447+
448+ ast .Inspect (file , func (n ast.Node ) bool {
449+ switch n := n .(type ) {
450+ case * ast.Ident :
451+ if n .Name == id .Name && info .ObjectOf (n ) == obj {
452+ highlight (n )
453+ }
454+
455+ case * ast.ImportSpec :
456+ pkgname , ok := ImportedPkgName (info , n )
457+ if ok && pkgname == obj {
458+ if n .Name != nil {
459+ highlight (n .Name )
460+ } else {
461+ highlight (n )
462+ }
463+ }
471464 }
472- return false
465+ return true
473466 })
474467}
475468
476- func highlightImport (obj * types.PkgName , imp * ast.ImportSpec , result map [posRange ]struct {}) {
477- if imp .Name != nil || imp .Path == nil {
478- return
479- }
480- if ! strings .Contains (imp .Path .Value , obj .Name ()) {
481- return
469+ // ImportedPkgName returns the PkgName object declared by an ImportSpec.
470+ // TODO(adonovan): make this a method of types.Info.
471+ func ImportedPkgName (info * types.Info , imp * ast.ImportSpec ) (* types.PkgName , bool ) {
472+ var obj types.Object
473+ if imp .Name != nil {
474+ obj = info .Defs [imp .Name ]
475+ } else {
476+ obj = info .Implicits [imp ]
482477 }
483- result [posRange {start : imp .Path .Pos (), end : imp .Path .End ()}] = struct {}{}
478+ pkgname , ok := obj .(* types.PkgName )
479+ return pkgname , ok
484480}
0 commit comments