Skip to content

Commit 0be5d8d

Browse files
committed
Use import qualifier instead of type extractor in order to adjust package name according to imports
1 parent 4bfb633 commit 0be5d8d

File tree

2 files changed

+97
-139
lines changed

2 files changed

+97
-139
lines changed

gopls/internal/golang/stub.go

+90-71
Original file line numberDiff line numberDiff line change
@@ -96,76 +96,11 @@ func insertDeclsAfter(ctx context.Context, snapshot *cache.Snapshot, mp *metadat
9696
return nil, nil, bug.Errorf("can't find metadata for file %s among dependencies of %s", declPGF.URI, mp)
9797
}
9898

99-
// Build import environment for the declaring file.
100-
// (typesinternal.FileQualifier works only for complete
101-
// import mappings, and requires types.)
102-
importEnv := make(map[ImportPath]string) // value is local name
103-
for _, imp := range declPGF.File.Imports {
104-
importPath := metadata.UnquoteImportPath(imp)
105-
var name string
106-
if imp.Name != nil {
107-
name = imp.Name.Name
108-
if name == "_" {
109-
continue
110-
} else if name == "." {
111-
name = "" // see types.Qualifier
112-
}
113-
} else {
114-
// Use the correct name from the metadata of the imported
115-
// package---not a guess based on the import path.
116-
mp := snapshot.Metadata(declMeta.DepsByImpPath[importPath])
117-
if mp == nil {
118-
continue // can't happen?
119-
}
120-
name = string(mp.Name)
121-
}
122-
importEnv[importPath] = name // latest alias wins
99+
newImports := make([]newImport, 0, len(declPGF.File.Imports))
100+
qual := newNamedImportQual(declPGF, snapshot, declMeta, sym, &newImports)
101+
if len(newImports) != 0 {
102+
println()
123103
}
124-
125-
// Create a package name qualifier that uses the
126-
// locally appropriate imported package name.
127-
// It records any needed new imports.
128-
// TODO(adonovan): factor with golang.FormatVarType?
129-
//
130-
// Prior to CL 469155 this logic preserved any renaming
131-
// imports from the file that declares the interface
132-
// method--ostensibly the preferred name for imports of
133-
// frequently renamed packages such as protobufs.
134-
// Now we use the package's declared name. If this turns out
135-
// to be a mistake, then use parseHeader(si.iface.Pos()).
136-
//
137-
type newImport struct{ name, importPath string }
138-
var newImports []newImport // for AddNamedImport
139-
qual := func(pkg *types.Package) string {
140-
// TODO(adonovan): don't ignore vendor prefix.
141-
//
142-
// Ignore the current package import.
143-
if pkg.Path() == sym.Pkg().Path() {
144-
return ""
145-
}
146-
147-
importPath := ImportPath(pkg.Path())
148-
name, ok := importEnv[importPath]
149-
if !ok {
150-
// Insert new import using package's declared name.
151-
//
152-
// TODO(adonovan): resolve conflict between declared
153-
// name and existing file-level (declPGF.File.Imports)
154-
// or package-level (sym.Pkg.Scope) decls by
155-
// generating a fresh name.
156-
name = pkg.Name()
157-
importEnv[importPath] = name
158-
new := newImport{importPath: string(importPath)}
159-
// For clarity, use a renaming import whenever the
160-
// local name does not match the path's last segment.
161-
if name != pathpkg.Base(trimVersionSuffix(new.importPath)) {
162-
new.name = name
163-
}
164-
newImports = append(newImports, new)
165-
}
166-
return name
167-
}
168-
169104
// Compute insertion point for new declarations:
170105
// after the top-level declaration enclosing the (package-level) type.
171106
insertOffset, err := safetoken.Offset(declPGF.Tok, declPGF.File.End())
@@ -252,7 +187,7 @@ func trimVersionSuffix(path string) string {
252187
return path
253188
}
254189

255-
func insertStructField(ctx context.Context, snapshot *cache.Snapshot, meta *metadata.Package, fieldInfo *stubmethods.StructFieldInfo) (*token.FileSet, *analysis.SuggestedFix, error) {
190+
func insertStructField(ctx context.Context, snapshot *cache.Snapshot, mp *metadata.Package, fieldInfo *stubmethods.StructFieldInfo) (*token.FileSet, *analysis.SuggestedFix, error) {
256191
if fieldInfo == nil {
257192
return nil, nil, fmt.Errorf("no field info provided")
258193
}
@@ -284,6 +219,15 @@ func insertStructField(ctx context.Context, snapshot *cache.Snapshot, meta *meta
284219
return nil, nil, fmt.Errorf("could not find struct definition")
285220
}
286221

222+
// Find metadata for the symbol's declaring package
223+
// as we'll need its import mapping.
224+
declMeta := findFileInDeps(snapshot, mp, declPGF.URI)
225+
if declMeta == nil {
226+
return nil, nil, bug.Errorf("can't find metadata for file %s among dependencies of %s", declPGF.URI, mp)
227+
}
228+
229+
qual := newNamedImportQual(declPGF, snapshot, declMeta, fieldInfo.Named.Obj(), new([]newImport))
230+
287231
// find the position to insert the new field (end of struct fields)
288232
insertPos := structType.Fields.Closing - 1
289233
if insertPos == structType.Fields.Opening {
@@ -292,7 +236,7 @@ func insertStructField(ctx context.Context, snapshot *cache.Snapshot, meta *meta
292236
}
293237

294238
var buf bytes.Buffer
295-
if err := fieldInfo.Emit(&buf, types.RelativeTo(fieldInfo.Named.Obj().Pkg())); err != nil {
239+
if err := fieldInfo.Emit(&buf, qual); err != nil {
296240
return nil, nil, err
297241
}
298242

@@ -312,3 +256,78 @@ func insertStructField(ctx context.Context, snapshot *cache.Snapshot, meta *meta
312256
TextEdits: []analysis.TextEdit{textEdit},
313257
}, nil
314258
}
259+
260+
type newImport struct {
261+
name string
262+
importPath string
263+
}
264+
265+
func newNamedImportQual(declPGF *parsego.File, snapshot *cache.Snapshot, declMeta *metadata.Package, sym types.Object, newImports *[]newImport) func(*types.Package) string {
266+
// Build import environment for the declaring file.
267+
// (typesinternal.FileQualifier works only for complete
268+
// import mappings, and requires types.)
269+
importEnv := make(map[ImportPath]string) // value is local name
270+
for _, imp := range declPGF.File.Imports {
271+
importPath := metadata.UnquoteImportPath(imp)
272+
var name string
273+
if imp.Name != nil {
274+
name = imp.Name.Name
275+
if name == "_" {
276+
continue
277+
} else if name == "." {
278+
name = "" // see types.Qualifier
279+
}
280+
} else {
281+
// Use the correct name from the metadata of the imported
282+
// package---not a guess based on the import path.
283+
mp := snapshot.Metadata(declMeta.DepsByImpPath[importPath])
284+
if mp == nil {
285+
continue // can't happen?
286+
}
287+
name = string(mp.Name)
288+
}
289+
importEnv[importPath] = name // latest alias wins
290+
}
291+
292+
// Create a package name qualifier that uses the
293+
// locally appropriate imported package name.
294+
// It records any needed new imports.
295+
// TODO(adonovan): factor with golang.FormatVarType?
296+
//
297+
// Prior to CL 469155 this logic preserved any renaming
298+
// imports from the file that declares the interface
299+
// method--ostensibly the preferred name for imports of
300+
// frequently renamed packages such as protobufs.
301+
// Now we use the package's declared name. If this turns out
302+
// to be a mistake, then use parseHeader(si.iface.Pos()).
303+
//
304+
return func(pkg *types.Package) string {
305+
// TODO(adonovan): don't ignore vendor prefix.
306+
//
307+
// Ignore the current package import.
308+
if pkg.Path() == sym.Pkg().Path() {
309+
return ""
310+
}
311+
312+
importPath := ImportPath(pkg.Path())
313+
name, ok := importEnv[importPath]
314+
if !ok {
315+
// Insert new import using package's declared name.
316+
//
317+
// TODO(adonovan): resolve conflict between declared
318+
// name and existing file-level (declPGF.File.Imports)
319+
// or package-level (sym.Pkg.Scope) decls by
320+
// generating a fresh name.
321+
name = pkg.Name()
322+
importEnv[importPath] = name
323+
new := newImport{importPath: string(importPath)}
324+
// For clarity, use a renaming import whenever the
325+
// local name does not match the path's last segment.
326+
if name != pathpkg.Base(trimVersionSuffix(new.importPath)) {
327+
new.name = name
328+
}
329+
*newImports = append(*newImports, new)
330+
}
331+
return name
332+
}
333+
}

gopls/internal/util/typesutil/typesutil.go

+7-68
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"go/ast"
1010
"go/token"
1111
"go/types"
12-
"strconv"
1312
"strings"
1413

1514
"golang.org/x/tools/gopls/internal/util/astutil"
@@ -235,88 +234,28 @@ func EnclosingSignature(path []ast.Node, info *types.Info) *types.Signature {
235234
return nil
236235
}
237236

238-
func typeFromExprAssignExpr(exprs, opposite []ast.Expr, info *types.Info, path []ast.Node, pos token.Pos, validType func(t types.Type) types.Type) []types.Type {
237+
func typeFromExprAssignExpr(exprs, opposites []ast.Expr, info *types.Info, path []ast.Node, pos token.Pos, validType func(t types.Type) types.Type) []types.Type {
239238
typs := make([]types.Type, 0)
240239
// Append all lhs's type
241240
if len(exprs) == 1 {
242-
for _, lhs := range opposite {
243-
t := adjustedPackageType(info, path, lhs)
241+
for i := range opposites {
242+
t := info.TypeOf(opposites[i])
244243
typs = append(typs, validType(t))
245244
}
246245
return typs
247246
}
248247
// Lhs and Rhs counts do not match, give up
249-
if len(opposite) != len(exprs) {
248+
if len(opposites) != len(exprs) {
250249
return typs
251250
}
252251
// Append corresponding index of lhs's type
253-
for i, rhs := range exprs {
254-
if rhs.Pos() <= pos && pos <= rhs.End() {
255-
t := adjustedPackageType(info, path, opposite[i])
252+
for i := range exprs {
253+
if exprs[i].Pos() <= pos && pos <= exprs[i].End() {
254+
t := info.TypeOf(opposites[i])
256255
typs = append(typs, validType(t))
257256
break
258257
}
259258
}
260259

261260
return typs
262261
}
263-
264-
func adjustedPackageType(info *types.Info, path []ast.Node, expr ast.Expr) types.Type {
265-
t := info.TypeOf(expr)
266-
if t == nil {
267-
return nil
268-
}
269-
270-
if containsInvalid(t) {
271-
return types.Universe.Lookup("any").Type()
272-
}
273-
274-
t = types.Default(t)
275-
if named, ok := t.(*types.Named); ok {
276-
if pkg := named.Obj().Pkg(); pkg != nil {
277-
// find the file in the path that contains this assignment
278-
var file *ast.File
279-
for _, n := range path {
280-
if f, ok := n.(*ast.File); ok {
281-
file = f
282-
break
283-
}
284-
}
285-
286-
if file != nil {
287-
for i := range file.Scope.Objects {
288-
// named is in the scope and requires no package
289-
if named.Obj().Name() == file.Scope.Objects[i].Name {
290-
return t
291-
}
292-
}
293-
294-
// look for any import spec that imports this package
295-
var pkgName string
296-
for _, imp := range file.Imports {
297-
if path, _ := strconv.Unquote(imp.Path.Value); path == pkg.Path() {
298-
// use the alias if specified, otherwise use package name
299-
if imp.Name != nil {
300-
pkgName = imp.Name.Name
301-
} else {
302-
pkgName = pkg.Name()
303-
}
304-
break
305-
}
306-
}
307-
// fallback to package name if no import found
308-
if pkgName == "" {
309-
pkgName = pkg.Name()
310-
}
311-
312-
// create new package with the correct name (either alias or original)
313-
newPkg := types.NewPackage(pkgName, pkgName)
314-
newName := types.NewTypeName(named.Obj().Pos(), newPkg, named.Obj().Name(), nil)
315-
t = types.NewNamed(newName, named.Underlying(), nil)
316-
}
317-
}
318-
return t
319-
}
320-
321-
return t
322-
}

0 commit comments

Comments
 (0)