Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions internal/testdata/snapshots/input/pr201/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# pr201 --- Import alias resolution

This test covers import alias handling introduced in PR #201, addressing [#34].

Import aliases are resolved as **global references** to the original package
symbol rather than local definitions. This means:

- `h` in `import h "net/http"` emits a reference to `net/http`, not a local
definition.
- All subsequent usages of `h` (e.g., `h.StatusOK`) also reference `net/http`
directly.
- A "Find references" lookup on the original `package http` statement returns
every individual usage site, not just the import line.

This applies consistently to all aliased imports: same-name aliases, renamed
aliases, and aliases used across multiple files in the same package.

While this approach differs from gopls, which treats import aliases as local
definitions, SCIP is a protocol serving a greater number of languages. Resolving
aliases as global references is a more universal approach --- it matches how
most languages treat imports and ensures consistent cross-language behavior.

[#34]: https://github.com/sourcegraph/scip-go/issues/34
3 changes: 3 additions & 0 deletions internal/testdata/snapshots/input/pr201/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module sg/pr201

go 1.23
20 changes: 20 additions & 0 deletions internal/testdata/snapshots/input/pr201/imports.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package pr201

import (
"context"
"net/http"

_ "embed"

. "strings"

fmt "fmt"
h "net/http"
)

func UseImports() {
fmt.Println(context.Background())
_ = http.StatusOK
_ = h.DefaultClient
_ = Contains("hello", "ell")
}
46 changes: 46 additions & 0 deletions internal/testdata/snapshots/output/pr201/imports.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package pr201
// ^^^^^ definition 0.1.test `sg/pr201`/

import (
"context"
// ^^^^^^^ reference github.com/golang/go/src go1.22 context/
"net/http"
// ^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/

_ "embed"
// ^^^^^ reference github.com/golang/go/src go1.22 embed/

. "strings"
// ^^^^^^^ reference github.com/golang/go/src go1.22 strings/

fmt "fmt"
// ^^^ reference github.com/golang/go/src go1.22 fmt/
// ^^^ reference github.com/golang/go/src go1.22 fmt/
h "net/http"
// ^ reference github.com/golang/go/src go1.22 `net/http`/
// ^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/
)

//⌄ enclosing_range_start 0.1.test `sg/pr201`/UseImports().
func UseImports() {
// ^^^^^^^^^^ definition 0.1.test `sg/pr201`/UseImports().
// documentation
// > ```go
// > func UseImports()
// > ```
fmt.Println(context.Background())
// ^^^ reference github.com/golang/go/src go1.22 fmt/
// ^^^^^^^ reference github.com/golang/go/src go1.22 fmt/Println().
// ^^^^^^^ reference github.com/golang/go/src go1.22 context/
// ^^^^^^^^^^ reference github.com/golang/go/src go1.22 context/Background().
_ = http.StatusOK
// ^^^^ reference github.com/golang/go/src go1.22 `net/http`/
// ^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/StatusOK.
_ = h.DefaultClient
// ^ reference github.com/golang/go/src go1.22 `net/http`/
// ^^^^^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/DefaultClient.
_ = Contains("hello", "ell")
// ^^^^^^^^ reference github.com/golang/go/src go1.22 strings/Contains().
}
//⌃ enclosing_range_end 0.1.test `sg/pr201`/UseImports().

Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// ^^^ reference github.com/golang/go/src go1.22 fmt/

replaced "github.com/example/original"
// ^^^^^^^^ definition local 0
// ^^^^^^^^ reference github.com/example/replaced 0.1.test `github.com/example/original`/
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ reference github.com/example/replaced 0.1.test `github.com/example/original`/
)

Expand All @@ -20,7 +20,7 @@
fmt.Println(replaced.DefaultConfig)
// ^^^ reference github.com/golang/go/src go1.22 fmt/
// ^^^^^^^ reference github.com/golang/go/src go1.22 fmt/Println().
// ^^^^^^^^ reference local 0
// ^^^^^^^^ reference github.com/example/replaced 0.1.test `github.com/example/original`/
// ^^^^^^^^^^^^^ reference github.com/example/replaced 0.1.test `github.com/example/original`/DefaultConfig.
}
//⌃ enclosing_range_end 0.1.test `sg/replace-directives`/Something().
Expand Down
4 changes: 2 additions & 2 deletions internal/testdata/snapshots/output/testdata/named_import.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
. "fmt"
// ^^^ reference github.com/golang/go/src go1.22 fmt/
h "net/http"
// ^ definition local 0
// ^ reference github.com/golang/go/src go1.22 `net/http`/
// ^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/
)

Expand All @@ -18,7 +18,7 @@
// > ```
Println(h.CanonicalHeaderKey("accept-encoding"))
// ^^^^^^^ reference github.com/golang/go/src go1.22 fmt/Println().
// ^ reference local 0
// ^ reference github.com/golang/go/src go1.22 `net/http`/
// ^^^^^^^^^^^^^^^^^^ reference github.com/golang/go/src go1.22 `net/http`/CanonicalHeaderKey().
}
//⌃ enclosing_range_end 0.1.test `sg/testdata`/Example().
Expand Down
76 changes: 28 additions & 48 deletions internal/visitors/visitor_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,15 @@ func NewFileVisitor(
}

return &fileVisitor{
doc: doc,
pkg: pkg,
file: file,
pkgLookup: pkgLookup,
locals: map[token.Pos]lookup.Local{},
pkgSymbols: pkgSymbols,
globalSymbols: globalSymbols,
occurrences: occurrences,
overrides: struct {
caseClauses map[token.Pos]types.Object
pkgNameOverride map[newtypes.PackageID]string
}{
caseClauses: caseClauses,
pkgNameOverride: map[newtypes.PackageID]string{},
},
doc: doc,
pkg: pkg,
file: file,
pkgLookup: pkgLookup,
locals: map[token.Pos]lookup.Local{},
pkgSymbols: pkgSymbols,
globalSymbols: globalSymbols,
occurrences: occurrences,
caseClauses: caseClauses,
enclosingNodeMap: enclosingNodeMap(file),
}
}
Expand Down Expand Up @@ -89,15 +83,8 @@ type fileVisitor struct {
// occurrences in this file
occurrences []*scip.Occurrence

// Overriding Definition Behvaior:
overrides struct {
// Case clauses have to map particular positions to different types
caseClauses map[token.Pos]types.Object

// maps tokens for package declaration to a local var,
// if ImportSpec.Name is not nil. Otherwise, just use package directly
pkgNameOverride map[newtypes.PackageID]string
}
// caseClauses maps particular positions to different types for case clauses
caseClauses map[token.Pos]types.Object

// enclosingNodeMap maps certain nodes to their enclosing nodes for
// enclosing range computation.
Expand Down Expand Up @@ -136,16 +123,14 @@ func (v *fileVisitor) Visit(n ast.Node) ast.Visitor {
return nil
}

if node.Name != nil && node.Name.Name != "." {
pkgAlias := v.pkg.TypesInfo.Defs[node.Name]
symName := v.createNewLocalSymbol(node.Name.Pos(), pkgAlias)
if node.Name != nil && node.Name.Name != "." && node.Name.Name != "_" {
rangeFromName := symbols.RangeFromName(
v.pkg.Fset.Position(node.Name.Pos()), node.Name.Name, false)
v.NewDefinition(symName, rangeFromName, nil)

// Save package name override, so that we use the new local symbol
// within this file
v.overrides.pkgNameOverride[newtypes.GetID(importedPackage)] = symName
if rangeFromName != nil {
if sym, ok := v.globalSymbols.GetPkgSymbol(importedPackage); ok {
v.AppendSymbolReference(sym, rangeFromName, nil)
}
}
}

position := v.pkg.Fset.Position(node.Path.Pos())
Expand All @@ -163,22 +148,17 @@ func (v *fileVisitor) Visit(n ast.Node) ast.Visitor {
case *types.PkgName:
startPosition := v.pkg.Fset.Position(ident.Pos())
endPosition := v.pkg.Fset.Position(ident.End())
pkgID := newtypes.GetFromTypesPackage(sel.Imported())

var symbolName string
if overrideSymbol, ok := v.overrides.pkgNameOverride[pkgID]; ok {
symbolName = overrideSymbol
} else {
sym, ok := v.globalSymbols.GetPkgSymbolByID(pkgID)
if !ok {
slog.Debug(fmt.Sprintf("Missing symbol for package: %s", sel.Imported().Path()))
return nil
}

symbolName = sym

packageID := newtypes.GetFromTypesPackage(sel.Imported())
sym, ok := v.globalSymbols.GetPkgSymbolByID(packageID)
if !ok {
slog.Debug(fmt.Sprintf(
"Missing symbol for package: %s", sel.Imported().Path()))
return nil
}

v.AppendSymbolReference(symbolName, scipRange(startPosition, endPosition, sel), nil)
symRange := scipRange(startPosition, endPosition, sel)
v.AppendSymbolReference(sym, symRange, nil)

// Then walk the selection
ast.Walk(v, node.Sel)
Expand Down Expand Up @@ -209,7 +189,7 @@ func (v *fileVisitor) Visit(n ast.Node) ast.Visitor {
endPosition := v.pkg.Fset.Position(node.End())

// Short circuit on case clauses
if obj, ok := v.overrides.caseClauses[node.Pos()]; ok {
if obj, ok := v.caseClauses[node.Pos()]; ok {
symName := v.createNewLocalSymbol(obj.Pos(), obj)
v.NewDefinition(symName, scipRange(startPosition, endPosition, obj), nil)
return nil
Expand Down Expand Up @@ -243,7 +223,7 @@ func (v *fileVisitor) Visit(n ast.Node) ast.Visitor {
if localSymbol, ok := v.locals[ref.Pos()]; ok {
symbol = localSymbol.Symbol

if _, ok := v.overrides.caseClauses[ref.Pos()]; ok {
if _, ok := v.caseClauses[ref.Pos()]; ok {
overrideType = v.pkg.TypesInfo.TypeOf(node)
}
} else {
Expand Down
Loading