@@ -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.
4044func 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