Beispiel #1
0
// checkInPackageBlock performs safety checks for renames of
// func/var/const/type objects in the package block.
func (e *Export) checkInPackageBlock(from types.Object, to string) {
	info := e.u.pkgInfo
	lexinfo := lexical.Structure(e.u.prog.Fset, from.Pkg(), &info.Info, info.Files)

	// We don't rename anything in the package block to init, as that might
	// conflict or otherwise break stuff
	if to == "init" {
		e.Conflicting = true
		return
	}

	// Check for conflicts between package block and all file blocks.
	for _, f := range info.Files {
		if _, b := lexinfo.Blocks[f].Lookup(to); b == lexinfo.Blocks[f] {
			e.Conflicting = true
			return
		}
	}

	if f, ok := from.(*types.Func); ok && recv(f) == nil {
		e.checkFunction(f, to)
		if e.Conflicting {
			return
		}
	}

	// Check for conflicts in lexical scope.
	// Do not need to check all imported packages:
	// Since it's unnecessarily exported, no one else is going to be sad if I unexport it!
	e.checkInLexicalScope(from, to)
}
Beispiel #2
0
// checkInPackageBlock performs safety checks for renames of
// func/var/const/type objects in the package block.
func (r *renamer) checkInPackageBlock(from types.Object) {
	// Check that there are no references to the name from another
	// package if the renaming would make it unexported.
	if ast.IsExported(from.Name()) && !ast.IsExported(r.to) {
		for pkg, info := range r.packages {
			if pkg == from.Pkg() {
				continue
			}
			if id := someUse(info, from); id != nil &&
				!r.checkExport(id, pkg, from) {
				break
			}
		}
	}

	info := r.packages[from.Pkg()]
	lexinfo := lexical.Structure(r.iprog.Fset, from.Pkg(), &info.Info, info.Files)

	// Check that in the package block, "init" is a function, and never referenced.
	if r.to == "init" {
		kind := objectKind(from)
		if kind == "func" {
			// Reject if intra-package references to it exist.
			if refs := lexinfo.Refs[from]; len(refs) > 0 {
				r.errorf(from.Pos(),
					"renaming this func %q to %q would make it a package initializer",
					from.Name(), r.to)
				r.errorf(refs[0].Id.Pos(), "\tbut references to it exist")
			}
		} else {
			r.errorf(from.Pos(), "you cannot have a %s at package level named %q",
				kind, r.to)
		}
	}

	// Check for conflicts between package block and all file blocks.
	for _, f := range info.Files {
		if prev, b := lexinfo.Blocks[f].Lookup(r.to); b == lexinfo.Blocks[f] {
			r.errorf(from.Pos(), "renaming this %s %q to %q would conflict",
				objectKind(from), from.Name(), r.to)
			r.errorf(prev.Pos(), "\twith this %s",
				objectKind(prev))
			return // since checkInPackageBlock would report redundant errors
		}
	}

	// Check for conflicts in lexical scope.
	if from.Exported() {
		for _, info := range r.packages {
			r.checkInLexicalScope(from, info)
		}
	} else {
		r.checkInLexicalScope(from, info)
	}
}
Beispiel #3
0
func (r *Unexporter) lexInfo(info *loader.PackageInfo) *lexical.Info {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	if lexinfo := r.lexinfos[info]; lexinfo != nil {
		return lexinfo
	} else {
		lexinfo := lexical.Structure(r.iprog.Fset, info.Pkg, &info.Info, info.Files)
		r.lexinfos[info] = lexinfo
		return lexinfo
	}
}
Beispiel #4
0
func (e *Export) checkInLexicalScope(from types.Object, to string) {
	info := e.u.pkgInfo
	lexinfo := lexical.Structure(e.u.prog.Fset, info.Pkg, &info.Info, info.Files)

	b := lexinfo.Defs[from] // the block defining the 'from' object
	if b != nil {
		to, toBlock := b.Lookup(to)
		if toBlock == b {
			e.Conflicting = true
			return // same-block conflict
		} else if toBlock != nil {
			for _, ref := range lexinfo.Refs[to] {
				if obj, _ := ref.Env.Lookup(from.Name()); obj == from {
					e.Conflicting = true
					return // super-block conflict
				}
			}
		}
	}

	// Check for sub-block conflict.
	// Is there an intervening definition of r.to between
	// the block defining 'from' and some reference to it?
	for _, ref := range lexinfo.Refs[from] {
		_, fromBlock := ref.Env.Lookup(from.Name())
		fromDepth := fromBlock.Depth()

		to, toBlock := ref.Env.Lookup(to)
		if to != nil {
			// sub-block conflict
			if toBlock.Depth() > fromDepth {
				e.Conflicting = true
				return
			}
		}
	}

	// Renaming a type that is used as an embedded field
	// requires renaming the field too. e.g.
	// 	type T int // if we rename this to U..
	// 	var s struct {T}
	// 	print(s.T) // ...this must change too
	if _, ok := from.(*types.TypeName); ok {
		for id, obj := range info.Uses {
			if obj == from {
				if field := info.Defs[id]; field != nil {
					e.check(field, to)
				}
			}
		}
	}
}
Beispiel #5
0
// checkInLexicalScope performs safety checks that a renaming does not
// change the lexical reference structure of the specified package.
//
// For objects in lexical scope, there are three kinds of conflicts:
// same-, sub-, and super-block conflicts.  We will illustrate all three
// using this example:
//
//      var x int
//	var z int
//
//	func f(y int) {
//		print(x)
//		print(y)
//	}
//
// Renaming x to z encounters a SAME-BLOCK CONFLICT, because an object
// with the new name already exists, defined in the same lexical block
// as the old object.
//
// Renaming x to y encounters a SUB-BLOCK CONFLICT, because there exists
// a reference to x from within (what would become) a hole in its scope.
// The definition of y in an (inner) sub-block would cast a shadow in
// the scope of the renamed variable.
//
// Renaming y to x encounters a SUPER-BLOCK CONFLICT.  This is the
// converse situation: there is an existing definition of the new name
// (x) in an (enclosing) super-block, and the renaming would create a
// hole in its scope, within which there exist references to it.  The
// new name casts a shadow in scope of the existing definition of x in
// the super-block.
//
// Removing the old name (and all references to it) is always safe, and
// requires no checks.
//
func (r *renamer) checkInLexicalScope(from types.Object, info *loader.PackageInfo) {
	lexinfo := lexical.Structure(r.iprog.Fset, info.Pkg, &info.Info, info.Files)

	b := lexinfo.Defs[from] // the block defining the 'from' object
	if b != nil {
		to, toBlock := b.Lookup(r.to)
		if toBlock == b {
			// same-block conflict
			r.errorf(from.Pos(), "renaming this %s %q to %q",
				objectKind(from), from.Name(), r.to)
			r.errorf(to.Pos(), "\tconflicts with %s in same block",
				objectKind(to))
			return
		} else if toBlock != nil {
			// Check for super-block conflict.
			// The name r.to is defined in a superblock.
			// Is that name referenced from within this block?
			for _, ref := range lexinfo.Refs[to] {
				if obj, _ := ref.Env.Lookup(from.Name()); obj == from {
					// super-block conflict
					r.errorf(from.Pos(), "renaming this %s %q to %q",
						objectKind(from), from.Name(), r.to)
					r.errorf(ref.Id.Pos(), "\twould shadow this reference")
					r.errorf(to.Pos(), "\tto the %s declared here",
						objectKind(to))
					return
				}
			}
		}
	}

	// Check for sub-block conflict.
	// Is there an intervening definition of r.to between
	// the block defining 'from' and some reference to it?
	for _, ref := range lexinfo.Refs[from] {
		// TODO(adonovan): think about dot imports.
		// (Is b == fromBlock an invariant?)
		_, fromBlock := ref.Env.Lookup(from.Name())
		fromDepth := fromBlock.Depth()

		to, toBlock := ref.Env.Lookup(r.to)
		if to != nil {
			// sub-block conflict
			if toBlock.Depth() > fromDepth {
				r.errorf(from.Pos(), "renaming this %s %q to %q",
					objectKind(from), from.Name(), r.to)
				r.errorf(ref.Id.Pos(), "\twould cause this reference to become shadowed")
				r.errorf(to.Pos(), "\tby this intervening %s definition",
					objectKind(to))
				return
			}
		}
	}

	// Renaming a type that is used as an embedded field
	// requires renaming the field too. e.g.
	// 	type T int // if we rename this to U..
	// 	var s struct {T}
	// 	print(s.T) // ...this must change too
	if _, ok := from.(*types.TypeName); ok {
		for id, obj := range info.Uses {
			if obj == from {
				if field := info.Defs[id]; field != nil {
					r.check(field)
				}
			}
		}
	}
}