aboutsummaryrefslogtreecommitdiff
path: root/refactor/lexical/lexical.go
blob: c6567e4dbb65f5a4f9f54fd93276a3d715c1414c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
// Package lexical computes the structure of the lexical environment,
// including the definition of and references to all universal,
// package-level, file-level and function-local entities.  It does not
// record qualified identifiers, labels, struct fields, or methods.
//
// It is intended for renaming and refactoring tools, which need a more
// precise understanding of identifier resolution than is available from
// the output of the type-checker alone.
//
// THIS INTERFACE IS EXPERIMENTAL AND MAY CHANGE OR BE REMOVED IN FUTURE.
//
package lexical // import "golang.org/x/tools/refactor/lexical"

// OVERVIEW
//
// As we traverse the AST, we build a "spaghetti stack" of Blocks,
// i.e. a tree with parent edges pointing to the root.  Each time we
// visit an identifier that's a reference into the lexical environment,
// we create and save an Environment, which captures the current mapping
// state of the Block; these are saved for the client.
//
// We don't bother recording non-lexical references.

// TODO(adonovan):
// - make it robust against syntax errors.  Audit all type assertions, etc.
// - better still, after the Go 1.4 thaw, move this into go/types.
//   I don't think it need be a big change since the visitor is already there;
//   we just need to records Environments.  lexical.Block is analogous
//   to types.Scope.

import (
	"fmt"
	"go/ast"
	"go/token"
	"os"
	"strconv"

	"golang.org/x/tools/go/types"
)

const trace = false

var logf = func(format string, args ...interface{}) {
	fmt.Fprintf(os.Stderr, format, args...)
}

// A Block is a level of the lexical environment, a tree of blocks.
// It maps names to objects.
//
type Block struct {
	kind   string   // one of universe package file func block if switch typeswitch case for range
	syntax ast.Node // syntax declaring the block (nil for universe and package) [needed?]

	parent   Environment
	bindings []types.Object // bindings in lexical order
	index    map[string]int // maps a name to the index of its binding, for fast lookup
}

// An Environment is a snapshot of a Block taken at a certain lexical
// position. It may contain bindings for fewer names than the
// (completed) block, or different bindings for names that are
// re-defined later in the block.
//
// For example, the lexical Block for the function f below contains a
// binding for the local var x, but the Environments captured by at the
// two print(x) calls differ: the first contains this binding, the
// second does not.  The first Environment contains a different binding
// for x: the string var defined in the package block, an ancestor.
//
//	var x string
// 	func f() {
//		print(x)
//		x := 1
//		print(x)
//	}
//
type Environment struct {
	block     *Block
	nbindings int // length of prefix of block.bindings that's visible
}

// Depth returns the depth of this block in the block tree.
// The universal block has depth 1, a package block 2, a file block 3, etc.
func (b *Block) Depth() int {
	if b == nil {
		return 0
	}
	return 1 + b.parent.block.Depth()
}

// env returns an Environment that is a snapshot of b's current state.
func (b *Block) env() Environment {
	return Environment{b, len(b.bindings)}
}

// Lookup returns the definition of name in the environment specified by
// env, and the Block that defines it, which may be an ancestor.
func (env Environment) Lookup(name string) (types.Object, *Block) {
	if env.block == nil {
		return nil, nil
	}
	return lookup(env.block, name, env.nbindings)
}

// nbindings specifies what prefix of b.bindings should be considered visible.
func lookup(b *Block, name string, nbindings int) (types.Object, *Block) {
	if b == nil {
		return nil, nil
	}
	if i, ok := b.index[name]; ok && i < nbindings {
		return b.bindings[i], b
	}

	parent := b.parent
	if parent.block == nil {
		return nil, nil
	}
	return lookup(parent.block, name, parent.nbindings)
}

// Lookup returns the definition of name in the environment specified by
// b, and the Block that defines it, which may be an ancestor.
func (b *Block) Lookup(name string) (types.Object, *Block) {
	return b.env().Lookup(name)
}

// Block returns the block of which this environment is a partial view.
func (env Environment) Block() *Block {
	return env.block
}

func (env Environment) String() string {
	return fmt.Sprintf("%s:%d", env.block, env.nbindings)
}

func (b *Block) String() string {
	var s string
	if b.parent.block != nil {
		s = b.parent.block.String()
		s += "."
	}
	return s + b.kind
}

var universe = &Block{kind: "universe", index: make(map[string]int)}

func init() {
	for i, name := range types.Universe.Names() {
		obj := types.Universe.Lookup(name)
		universe.bindings = append(universe.bindings, obj)
		universe.index[name] = i
	}
}

// -- resolver ---------------------------------------------------------

// A Reference provides the lexical environment for a given reference to
// an object in lexical scope.
type Reference struct {
	Id  *ast.Ident
	Env Environment
}

// resolver holds the state of the identifier resolution visitation:
// the package information, the result, and the current block.
type resolver struct {
	fset    *token.FileSet
	imports map[string]*types.Package
	pkg     *types.Package
	info    *types.Info

	// visitor state
	block *Block

	result *Info
}

func (r *resolver) setBlock(kind string, syntax ast.Node) *Block {
	b := &Block{
		kind:   kind,
		syntax: syntax,
		parent: r.block.env(),
		index:  make(map[string]int),
	}
	if syntax != nil {
		r.result.Blocks[syntax] = b
	}
	r.block = b
	return b
}

func (r *resolver) qualifier(pkg *types.Package) string {
	if pkg == r.pkg {
		return "" // unqualified intra-package reference
	}
	return pkg.Path()
}

func (r *resolver) use(id *ast.Ident, env Environment) {
	if id.Name == "_" {
		return // an error
	}
	obj, _ := env.Lookup(id.Name)
	if obj == nil {
		logf("%s: lookup of %s failed\n", r.fset.Position(id.Pos()), id.Name)
	} else if want := r.info.Uses[id]; obj != want {
		// sanity check against go/types resolver
		logf("%s: internal error: lookup of %s yielded wrong object: got %v (%s), want %v\n",
			r.fset.Position(id.Pos()), id.Name, types.ObjectString(obj, r.qualifier),
			r.fset.Position(obj.Pos()),
			want)
	}
	if trace {
		logf("use %s = %v in %s\n", id.Name, types.ObjectString(obj, r.qualifier), env)
	}

	r.result.Refs[obj] = append(r.result.Refs[obj], Reference{id, env})
}

func (r *resolver) define(b *Block, id *ast.Ident) {
	obj := r.info.Defs[id]
	if obj == nil {
		logf("%s: internal error: not a defining ident: %s\n",
			r.fset.Position(id.Pos()), id.Name)
		panic(id)
	}
	r.defineObject(b, id.Name, obj)

	// Objects (other than PkgName) defined at file scope
	// are also defined in the enclosing package scope.
	if _, ok := b.syntax.(*ast.File); ok {
		switch obj.(type) {
		default:
			r.defineObject(b.parent.block, id.Name, obj)
		case nil, *types.PkgName:
		}
	}
}

// Used for implicit objects created by some ImportSpecs and CaseClauses.
func (r *resolver) defineImplicit(b *Block, n ast.Node, name string) {
	obj := r.info.Implicits[n]
	if obj == nil {
		logf("%s: internal error: not an implicit definition: %T\n",
			r.fset.Position(n.Pos()), n)
	}
	r.defineObject(b, name, obj)
}

func (r *resolver) defineObject(b *Block, name string, obj types.Object) {
	if obj.Name() == "_" {
		return
	}
	i := len(b.bindings)
	b.bindings = append(b.bindings, obj)
	b.index[name] = i
	if trace {
		logf("def %s = %s in %s\n", name, types.ObjectString(obj, r.qualifier), b)
	}
	r.result.Defs[obj] = b
}

func (r *resolver) function(recv *ast.FieldList, typ *ast.FuncType, body *ast.BlockStmt, syntax ast.Node) {
	// Use all signature types in enclosing block.
	r.expr(typ)
	r.fieldList(recv, false)

	savedBlock := r.block // save
	r.setBlock("func", syntax)

	// Define all parameters/results, and visit the body, within the func block.
	r.fieldList(typ.Params, true)
	r.fieldList(typ.Results, true)
	r.fieldList(recv, true)
	if body != nil {
		r.stmtList(body.List)
	}

	r.block = savedBlock // restore
}

func (r *resolver) fieldList(list *ast.FieldList, def bool) {
	if list != nil {
		for _, f := range list.List {
			if def {
				for _, id := range f.Names {
					r.define(r.block, id)
				}
			} else {
				r.expr(f.Type)
			}
		}
	}
}

func (r *resolver) exprList(list []ast.Expr) {
	for _, x := range list {
		r.expr(x)
	}
}

func (r *resolver) expr(n ast.Expr) {
	switch n := n.(type) {
	case *ast.BadExpr:
	case *ast.BasicLit:
		// no-op

	case *ast.Ident:
		r.use(n, r.block.env())

	case *ast.Ellipsis:
		if n.Elt != nil {
			r.expr(n.Elt)
		}

	case *ast.FuncLit:
		r.function(nil, n.Type, n.Body, n)

	case *ast.CompositeLit:
		if n.Type != nil {
			r.expr(n.Type)
		}
		tv := r.info.Types[n]
		if _, ok := deref(tv.Type).Underlying().(*types.Struct); ok {
			for _, elt := range n.Elts {
				if kv, ok := elt.(*ast.KeyValueExpr); ok {
					r.expr(kv.Value)

					// Also uses field kv.Key (non-lexical)
					//  id := kv.Key.(*ast.Ident)
					//  obj := r.info.Uses[id]
					//  logf("use %s = %v (field)\n",
					// 	id.Name, types.ObjectString(obj, r.qualifier))
					// TODO make a fake FieldVal selection?
				} else {
					r.expr(elt)
				}
			}
		} else {
			r.exprList(n.Elts)
		}

	case *ast.ParenExpr:
		r.expr(n.X)

	case *ast.SelectorExpr:
		r.expr(n.X)

		// Non-lexical reference to field/method, or qualified identifier.
		// if sel, ok := r.info.Selections[n]; ok { // selection
		// 	switch sel.Kind() {
		// 	case types.FieldVal:
		// 		logf("use %s = %v (field)\n",
		// 			n.Sel.Name, types.ObjectString(sel.Obj(), r.qualifier))
		// 	case types.MethodExpr, types.MethodVal:
		// 		logf("use %s = %v (method)\n",
		// 			n.Sel.Name, types.ObjectString(sel.Obj(), r.qualifier))
		// 	}
		// } else { // qualified identifier
		// 	obj := r.info.Uses[n.Sel]
		// 	logf("use %s = %v (qualified)\n", n.Sel.Name, obj)
		// }

	case *ast.IndexExpr:
		r.expr(n.X)
		r.expr(n.Index)

	case *ast.SliceExpr:
		r.expr(n.X)
		if n.Low != nil {
			r.expr(n.Low)
		}
		if n.High != nil {
			r.expr(n.High)
		}
		if n.Max != nil {
			r.expr(n.Max)
		}

	case *ast.TypeAssertExpr:
		r.expr(n.X)
		if n.Type != nil {
			r.expr(n.Type)
		}

	case *ast.CallExpr:
		r.expr(n.Fun)
		r.exprList(n.Args)

	case *ast.StarExpr:
		r.expr(n.X)

	case *ast.UnaryExpr:
		r.expr(n.X)

	case *ast.BinaryExpr:
		r.expr(n.X)
		r.expr(n.Y)

	case *ast.KeyValueExpr:
		r.expr(n.Key)
		r.expr(n.Value)

	case *ast.ArrayType:
		if n.Len != nil {
			r.expr(n.Len)
		}
		r.expr(n.Elt)

	case *ast.StructType:
		// Use all the type names, but don't define any fields.
		r.fieldList(n.Fields, false)

	case *ast.FuncType:
		// Use all the type names, but don't define any vars.
		r.fieldList(n.Params, false)
		r.fieldList(n.Results, false)

	case *ast.InterfaceType:
		// Use all the type names, but don't define any methods.
		r.fieldList(n.Methods, false)

	case *ast.MapType:
		r.expr(n.Key)
		r.expr(n.Value)

	case *ast.ChanType:
		r.expr(n.Value)

	default:
		panic(n)
	}
}

func (r *resolver) stmtList(list []ast.Stmt) {
	for _, s := range list {
		r.stmt(s)
	}
}

func (r *resolver) stmt(n ast.Stmt) {
	switch n := n.(type) {
	case *ast.BadStmt:
	case *ast.EmptyStmt:
		// nothing to do

	case *ast.DeclStmt:
		decl := n.Decl.(*ast.GenDecl)
		for _, spec := range decl.Specs {
			switch spec := spec.(type) {
			case *ast.ValueSpec: // const or var
				if spec.Type != nil {
					r.expr(spec.Type)
				}
				r.exprList(spec.Values)
				for _, name := range spec.Names {
					r.define(r.block, name)
				}

			case *ast.TypeSpec:
				r.define(r.block, spec.Name)
				r.expr(spec.Type)
			}
		}

	case *ast.LabeledStmt:
		// Also defines label n.Label (non-lexical)
		r.stmt(n.Stmt)

	case *ast.ExprStmt:
		r.expr(n.X)

	case *ast.SendStmt:
		r.expr(n.Chan)
		r.expr(n.Value)

	case *ast.IncDecStmt:
		r.expr(n.X)

	case *ast.AssignStmt:
		if n.Tok == token.DEFINE {
			r.exprList(n.Rhs)
			for _, lhs := range n.Lhs {
				id := lhs.(*ast.Ident)
				if _, ok := r.info.Defs[id]; ok {
					r.define(r.block, id)
				} else {
					r.use(id, r.block.env())
				}
			}
		} else { // ASSIGN
			r.exprList(n.Lhs)
			r.exprList(n.Rhs)
		}

	case *ast.GoStmt:
		r.expr(n.Call)

	case *ast.DeferStmt:
		r.expr(n.Call)

	case *ast.ReturnStmt:
		r.exprList(n.Results)

	case *ast.BranchStmt:
		if n.Label != nil {
			// Also uses label n.Label (non-lexical)
		}

	case *ast.SelectStmt:
		r.stmtList(n.Body.List)

	case *ast.BlockStmt: // (explicit blocks only)
		savedBlock := r.block // save
		r.setBlock("block", n)
		r.stmtList(n.List)
		r.block = savedBlock // restore

	case *ast.IfStmt:
		savedBlock := r.block // save
		r.setBlock("if", n)
		if n.Init != nil {
			r.stmt(n.Init)
		}
		r.expr(n.Cond)
		r.stmt(n.Body) // new block
		if n.Else != nil {
			r.stmt(n.Else)
		}
		r.block = savedBlock // restore

	case *ast.CaseClause:
		savedBlock := r.block // save
		r.setBlock("case", n)
		if obj, ok := r.info.Implicits[n]; ok {
			// e.g.
			//   switch y := x.(type) {
			//   case T: // we declare an implicit 'var y T' in this block
			//   }
			r.defineImplicit(r.block, n, obj.Name())
		}
		r.exprList(n.List)
		r.stmtList(n.Body)
		r.block = savedBlock // restore

	case *ast.SwitchStmt:
		savedBlock := r.block // save
		r.setBlock("switch", n)
		if n.Init != nil {
			r.stmt(n.Init)
		}
		if n.Tag != nil {
			r.expr(n.Tag)
		}
		r.stmtList(n.Body.List)
		r.block = savedBlock // restore

	case *ast.TypeSwitchStmt:
		savedBlock := r.block // save
		r.setBlock("typeswitch", n)
		if n.Init != nil {
			r.stmt(n.Init)
		}
		if assign, ok := n.Assign.(*ast.AssignStmt); ok { // y := x.(type)
			r.expr(assign.Rhs[0]) // skip y: not a defining ident
		} else {
			r.stmt(n.Assign)
		}
		r.stmtList(n.Body.List)
		r.block = savedBlock // restore

	case *ast.CommClause:
		savedBlock := r.block // save
		r.setBlock("case", n)
		if n.Comm != nil {
			r.stmt(n.Comm)
		}
		r.stmtList(n.Body)
		r.block = savedBlock // restore

	case *ast.ForStmt:
		savedBlock := r.block // save
		r.setBlock("for", n)
		if n.Init != nil {
			r.stmt(n.Init)
		}
		if n.Cond != nil {
			r.expr(n.Cond)
		}
		if n.Post != nil {
			r.stmt(n.Post)
		}
		r.stmt(n.Body)
		r.block = savedBlock // restore

	case *ast.RangeStmt:
		r.expr(n.X)
		savedBlock := r.block // save
		r.setBlock("range", n)
		if n.Tok == token.DEFINE {
			if n.Key != nil {
				r.define(r.block, n.Key.(*ast.Ident))
			}
			if n.Value != nil {
				r.define(r.block, n.Value.(*ast.Ident))
			}
		} else {
			if n.Key != nil {
				r.expr(n.Key)
			}
			if n.Value != nil {
				r.expr(n.Value)
			}
		}
		r.stmt(n.Body)
		r.block = savedBlock // restore

	default:
		panic(n)
	}
}

func (r *resolver) doImport(s *ast.ImportSpec, fileBlock *Block) {
	path, _ := strconv.Unquote(s.Path.Value)
	pkg := r.imports[path]
	if s.Name == nil { // normal
		r.defineImplicit(fileBlock, s, pkg.Name())
	} else if s.Name.Name == "." { // dot import
		for _, name := range pkg.Scope().Names() {
			if ast.IsExported(name) {
				obj := pkg.Scope().Lookup(name)
				r.defineObject(fileBlock, name, obj)
			}
		}
	} else { // renaming import
		r.define(fileBlock, s.Name)
	}
}

func (r *resolver) doPackage(pkg *types.Package, files []*ast.File) {
	r.block = universe
	r.result.Blocks[nil] = universe

	r.result.PackageBlock = r.setBlock("package", nil)

	var fileBlocks []*Block

	// 1. Insert all package-level objects into file and package blocks.
	//    (PkgName objects are only inserted into file blocks.)
	for _, f := range files {
		r.block = r.result.PackageBlock
		fileBlock := r.setBlock("file", f) // package is not yet visible to file
		fileBlocks = append(fileBlocks, fileBlock)

		for _, d := range f.Decls {
			switch d := d.(type) {
			case *ast.GenDecl:
				for _, s := range d.Specs {
					switch s := s.(type) {
					case *ast.ImportSpec:
						r.doImport(s, fileBlock)

					case *ast.ValueSpec: // const or var
						for _, name := range s.Names {
							r.define(r.result.PackageBlock, name)
						}

					case *ast.TypeSpec:
						r.define(r.result.PackageBlock, s.Name)
					}
				}

			case *ast.FuncDecl:
				if d.Recv == nil { // function
					if d.Name.Name != "init" {
						r.define(r.result.PackageBlock, d.Name)
					}
				}
			}
		}
	}

	// 2. Now resolve bodies of GenDecls and FuncDecls.
	for i, f := range files {
		fileBlock := fileBlocks[i]
		fileBlock.parent = r.result.PackageBlock.env() // make entire package visible to this file

		for _, d := range f.Decls {
			r.block = fileBlock

			switch d := d.(type) {
			case *ast.GenDecl:
				for _, s := range d.Specs {
					switch s := s.(type) {
					case *ast.ValueSpec: // const or var
						if s.Type != nil {
							r.expr(s.Type)
						}
						r.exprList(s.Values)

					case *ast.TypeSpec:
						r.expr(s.Type)
					}
				}

			case *ast.FuncDecl:
				r.function(d.Recv, d.Type, d.Body, d)
			}
		}
	}

	r.block = nil
}

// An Info contains the lexical reference structure of a package.
type Info struct {
	Defs         map[types.Object]*Block      // maps each object to its defining lexical block
	Refs         map[types.Object][]Reference // maps each object to the set of references to it
	Blocks       map[ast.Node]*Block          // maps declaring syntax to block; nil => universe
	PackageBlock *Block                       // the package-level lexical block
}

// Structure computes the structure of the lexical environment of the
// package specified by (pkg, info, files).
//
// The info.{Types,Defs,Uses,Implicits} maps must have been populated
// by the type-checker
//
// fset is used for logging.
//
func Structure(fset *token.FileSet, pkg *types.Package, info *types.Info, files []*ast.File) *Info {
	r := resolver{
		fset:    fset,
		imports: make(map[string]*types.Package),
		result: &Info{
			Defs:   make(map[types.Object]*Block),
			Refs:   make(map[types.Object][]Reference),
			Blocks: make(map[ast.Node]*Block),
		},
		pkg:  pkg,
		info: info,
	}

	// Build import map for just this package.
	r.imports["unsafe"] = types.Unsafe
	for _, imp := range pkg.Imports() {
		r.imports[imp.Path()] = imp
	}

	r.doPackage(pkg, files)

	return r.result
}

// -- Plundered from golang.org/x/tools/go/ssa -----------------

// deref returns a pointer's element type; otherwise it returns typ.
func deref(typ types.Type) types.Type {
	if p, ok := typ.Underlying().(*types.Pointer); ok {
		return p.Elem()
	}
	return typ
}