Skip to content

Commit 277e1b1

Browse files
adonovangopherbot
authored andcommitted
internal/astutil/free: support FuncDecl
This is a necessary building block for detecting imports that are made redundant when a set of fixes are applied. For golang/go#72035 Change-Id: Iaecd1eb9edb552052ede70681e74ab3c40417b41 Reviewed-on: https://go-review.googlesource.com/c/tools/+/717801 Auto-Submit: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Robert Findley <rfindley@google.com>
1 parent ce95316 commit 277e1b1

File tree

2 files changed

+142
-126
lines changed

2 files changed

+142
-126
lines changed

internal/astutil/free/free.go

Lines changed: 72 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,17 @@ import (
3030
// a struct type, so freeishNames underapproximates: the resulting set
3131
// may omit names that are free lexical references.
3232
//
33+
// TODO(adonovan): includeComplitIdents is a crude hammer: the caller
34+
// may have partial or heuristic information about whether a given T
35+
// is struct type. Replace includeComplitIdents with a hook to query
36+
// the caller.
37+
//
3338
// The code is based on go/parser.resolveFile, but heavily simplified. Crucial
3439
// differences are:
3540
// - Instead of resolving names to their objects, this function merely records
3641
// whether they are free.
3742
// - Labels are ignored: they do not refer to values.
38-
// - This is never called on FuncDecls or ImportSpecs, so the function
39-
// panics if it sees one.
43+
// - This is never called on ImportSpecs, so the function panics if it sees one.
4044
func Names(n ast.Node, includeComplitIdents bool) map[string]bool {
4145
v := &freeVisitor{
4246
free: make(map[string]bool),
@@ -83,12 +87,12 @@ func (v *freeVisitor) Visit(n ast.Node) ast.Visitor {
8387

8488
// Expressions.
8589
case *ast.Ident:
86-
v.resolve(n)
90+
v.use(n)
8791

8892
case *ast.FuncLit:
8993
v.openScope()
9094
defer v.closeScope()
91-
v.walkFuncType(n.Type)
95+
v.walkFuncType(nil, n.Type)
9296
v.walkBody(n.Body)
9397

9498
case *ast.SelectorExpr:
@@ -103,7 +107,7 @@ func (v *freeVisitor) Visit(n ast.Node) ast.Visitor {
103107
case *ast.FuncType:
104108
v.openScope()
105109
defer v.closeScope()
106-
v.walkFuncType(n)
110+
v.walkFuncType(nil, n)
107111

108112
case *ast.CompositeLit:
109113
v.walk(n.Type)
@@ -117,7 +121,7 @@ func (v *freeVisitor) Visit(n ast.Node) ast.Visitor {
117121
if v.includeComplitIdents {
118122
// Over-approximate by treating both cases as potentially
119123
// free names.
120-
v.resolve(ident)
124+
v.use(ident)
121125
} else {
122126
// Under-approximate by ignoring potentially free names.
123127
}
@@ -145,13 +149,11 @@ func (v *freeVisitor) Visit(n ast.Node) ast.Visitor {
145149
}
146150

147151
case *ast.LabeledStmt:
148-
// ignore labels
149-
// TODO(jba): consider labels?
152+
// Ignore labels.
150153
v.walk(n.Stmt)
151154

152155
case *ast.BranchStmt:
153156
// Ignore labels.
154-
// TODO(jba): consider labels?
155157

156158
case *ast.BlockStmt:
157159
v.openScope()
@@ -180,13 +182,11 @@ func (v *freeVisitor) Visit(n ast.Node) ast.Visitor {
180182
v.walkBody(n.Body)
181183

182184
case *ast.TypeSwitchStmt:
185+
v.openScope()
186+
defer v.closeScope()
183187
if n.Init != nil {
184-
v.openScope()
185-
defer v.closeScope()
186188
v.walk(n.Init)
187189
}
188-
v.openScope()
189-
defer v.closeScope()
190190
v.walk(n.Assign)
191191
// We can use walkBody here because we don't track label scopes.
192192
v.walkBody(n.Body)
@@ -235,11 +235,10 @@ func (v *freeVisitor) Visit(n ast.Node) ast.Visitor {
235235
for _, spec := range n.Specs {
236236
spec := spec.(*ast.ValueSpec)
237237
walkSlice(v, spec.Values)
238-
if spec.Type != nil {
239-
v.walk(spec.Type)
240-
}
238+
v.walk(spec.Type)
241239
v.declare(spec.Names...)
242240
}
241+
243242
case token.TYPE:
244243
for _, spec := range n.Specs {
245244
spec := spec.(*ast.TypeSpec)
@@ -260,7 +259,14 @@ func (v *freeVisitor) Visit(n ast.Node) ast.Visitor {
260259
}
261260

262261
case *ast.FuncDecl:
263-
panic("encountered top-level function declaration in free analysis")
262+
if n.Recv == nil { // package-level function
263+
v.declare(n.Name)
264+
}
265+
v.openScope()
266+
defer v.closeScope()
267+
v.walkTypeParams(n.Type.TypeParams)
268+
v.walkFuncType(n.Recv, n.Type)
269+
v.walkBody(n.Body)
264270

265271
default:
266272
return v
@@ -269,67 +275,60 @@ func (v *freeVisitor) Visit(n ast.Node) ast.Visitor {
269275
return nil
270276
}
271277

272-
func (r *freeVisitor) openScope() {
273-
r.scope = &scope{map[string]bool{}, r.scope}
278+
func (v *freeVisitor) openScope() {
279+
v.scope = &scope{map[string]bool{}, v.scope}
274280
}
275281

276-
func (r *freeVisitor) closeScope() {
277-
r.scope = r.scope.outer
282+
func (v *freeVisitor) closeScope() {
283+
v.scope = v.scope.outer
278284
}
279285

280-
func (r *freeVisitor) walk(n ast.Node) {
286+
func (v *freeVisitor) walk(n ast.Node) {
281287
if n != nil {
282-
ast.Walk(r, n)
288+
ast.Walk(v, n)
283289
}
284290
}
285291

286-
// walkFuncType walks a function type. It is used for explicit
287-
// function types, like this:
288-
//
289-
// type RunFunc func(context.Context) error
290-
//
291-
// and function literals, like this:
292-
//
293-
// func(a, b int) int { return a + b}
294-
//
295-
// neither of which have type parameters.
296-
// Function declarations do involve type parameters, but we don't
297-
// handle them.
298-
func (r *freeVisitor) walkFuncType(typ *ast.FuncType) {
299-
// The order here doesn't really matter, because names in
300-
// a field list cannot appear in types.
301-
// (The situation is different for type parameters, for which
302-
// see [freeVisitor.walkTypeParams].)
303-
r.resolveFieldList(typ.Params)
304-
r.resolveFieldList(typ.Results)
305-
r.declareFieldList(typ.Params)
306-
r.declareFieldList(typ.Results)
292+
func (v *freeVisitor) walkFuncType(recv *ast.FieldList, typ *ast.FuncType) {
293+
// First use field types...
294+
if recv != nil {
295+
v.walkFieldTypes(recv)
296+
}
297+
v.walkFieldTypes(typ.Params)
298+
v.walkFieldTypes(typ.Results)
299+
300+
// ...then declare field names.
301+
if recv != nil {
302+
v.declareFieldNames(recv)
303+
}
304+
v.declareFieldNames(typ.Params)
305+
v.declareFieldNames(typ.Results)
307306
}
308307

309308
// walkTypeParams is like walkFieldList, but declares type parameters eagerly so
310309
// that they may be resolved in the constraint expressions held in the field
311310
// Type.
312-
func (r *freeVisitor) walkTypeParams(list *ast.FieldList) {
313-
r.declareFieldList(list)
314-
r.resolveFieldList(list)
311+
func (v *freeVisitor) walkTypeParams(list *ast.FieldList) {
312+
v.declareFieldNames(list)
313+
v.walkFieldTypes(list) // constraints
315314
}
316315

317-
func (r *freeVisitor) walkBody(body *ast.BlockStmt) {
316+
func (v *freeVisitor) walkBody(body *ast.BlockStmt) {
318317
if body == nil {
319318
return
320319
}
321-
walkSlice(r, body.List)
320+
walkSlice(v, body.List)
322321
}
323322

324-
func (r *freeVisitor) walkFieldList(list *ast.FieldList) {
323+
func (v *freeVisitor) walkFieldList(list *ast.FieldList) {
325324
if list == nil {
326325
return
327326
}
328-
r.resolveFieldList(list) // .Type may contain references
329-
r.declareFieldList(list) // .Names declares names
327+
v.walkFieldTypes(list) // .Type may contain references
328+
v.declareFieldNames(list) // .Names declares names
330329
}
331330

332-
func (r *freeVisitor) shortVarDecl(lhs []ast.Expr) {
331+
func (v *freeVisitor) shortVarDecl(lhs []ast.Expr) {
333332
// Go spec: A short variable declaration may redeclare variables provided
334333
// they were originally declared in the same block with the same type, and
335334
// at least one of the non-blank variables is new.
@@ -340,7 +339,7 @@ func (r *freeVisitor) shortVarDecl(lhs []ast.Expr) {
340339
// In a well-formed program each expr must be an identifier,
341340
// but be forgiving.
342341
if id, ok := x.(*ast.Ident); ok {
343-
r.declare(id)
342+
v.declare(id)
344343
}
345344
}
346345
}
@@ -351,42 +350,39 @@ func walkSlice[S ~[]E, E ast.Node](r *freeVisitor, list S) {
351350
}
352351
}
353352

354-
// resolveFieldList resolves the types of the fields in list.
355-
// The companion method declareFieldList declares the names of the fields.
356-
func (r *freeVisitor) resolveFieldList(list *ast.FieldList) {
357-
if list == nil {
358-
return
359-
}
360-
for _, f := range list.List {
361-
r.walk(f.Type)
353+
// walkFieldTypes resolves the types of the walkFieldTypes in list.
354+
// The companion method declareFieldList declares the names of the walkFieldTypes.
355+
func (v *freeVisitor) walkFieldTypes(list *ast.FieldList) {
356+
if list != nil {
357+
for _, f := range list.List {
358+
v.walk(f.Type)
359+
}
362360
}
363361
}
364362

365-
// declareFieldList declares the names of the fields in list.
363+
// declareFieldNames declares the names of the fields in list.
366364
// (Names in a FieldList always establish new bindings.)
367365
// The companion method resolveFieldList resolves the types of the fields.
368-
func (r *freeVisitor) declareFieldList(list *ast.FieldList) {
369-
if list == nil {
370-
return
371-
}
372-
for _, f := range list.List {
373-
r.declare(f.Names...)
366+
func (v *freeVisitor) declareFieldNames(list *ast.FieldList) {
367+
if list != nil {
368+
for _, f := range list.List {
369+
v.declare(f.Names...)
370+
}
374371
}
375372
}
376373

377-
// resolve marks ident as free if it is not in scope.
378-
// TODO(jba): rename: no resolution is happening.
379-
func (r *freeVisitor) resolve(ident *ast.Ident) {
380-
if s := ident.Name; s != "_" && !r.scope.defined(s) {
381-
r.free[s] = true
374+
// use marks ident as free if it is not in scope.
375+
func (v *freeVisitor) use(ident *ast.Ident) {
376+
if s := ident.Name; s != "_" && !v.scope.defined(s) {
377+
v.free[s] = true
382378
}
383379
}
384380

385381
// declare adds each non-blank ident to the current scope.
386-
func (r *freeVisitor) declare(idents ...*ast.Ident) {
382+
func (v *freeVisitor) declare(idents ...*ast.Ident) {
387383
for _, id := range idents {
388384
if id.Name != "_" {
389-
r.scope.names[id.Name] = true
385+
v.scope.names[id.Name] = true
390386
}
391387
}
392388
}

0 commit comments

Comments
 (0)