cmd/compile: report error for unexported name only once

Fixes #22921

Change-Id: If29bd962335ac7676ea4f379727db3d55ae1bf8e
Reviewed-on: https://go-review.googlesource.com/c/go/+/250177
Run-TryBot: Cuong Manh Le <cuong.manhle.vn@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
index cd64d9a..4f6fddd 100644
--- a/src/cmd/compile/internal/gc/dcl.go
+++ b/src/cmd/compile/internal/gc/dcl.go
@@ -297,6 +297,16 @@
 	return n
 }
 
+// importName is like oldname, but it reports an error if sym is from another package and not exported.
+func importName(sym *types.Sym) *Node {
+	n := oldname(sym)
+	if !types.IsExported(sym.Name) && sym.Pkg != localpkg {
+		n.SetDiag(true)
+		yyerror("cannot refer to unexported name %s.%s", sym.Pkg.Name, sym.Name)
+	}
+	return n
+}
+
 // := declarations
 func colasname(n *Node) bool {
 	switch n.Op {
diff --git a/src/cmd/compile/internal/gc/noder.go b/src/cmd/compile/internal/gc/noder.go
index 802aab2..590c1a1 100644
--- a/src/cmd/compile/internal/gc/noder.go
+++ b/src/cmd/compile/internal/gc/noder.go
@@ -653,7 +653,7 @@
 		obj := p.expr(expr.X)
 		if obj.Op == OPACK {
 			obj.Name.SetUsed(true)
-			return oldname(restrictlookup(expr.Sel.Value, obj.Name.Pkg))
+			return importName(obj.Name.Pkg.Lookup(expr.Sel.Value))
 		}
 		n := nodSym(OXDOT, obj, p.name(expr.Sel))
 		n.Pos = p.pos(expr) // lineno may have been changed by p.expr(expr.X)
@@ -857,7 +857,7 @@
 		p.setlineno(method)
 		var n *Node
 		if method.Name == nil {
-			n = p.nodSym(method, ODCLFIELD, oldname(p.packname(method.Type)), nil)
+			n = p.nodSym(method, ODCLFIELD, importName(p.packname(method.Type)), nil)
 		} else {
 			mname := p.name(method.Name)
 			sig := p.typeExpr(method.Type)
@@ -896,7 +896,7 @@
 			def.Name.SetUsed(true)
 			pkg = def.Name.Pkg
 		}
-		return restrictlookup(expr.Sel.Value, pkg)
+		return pkg.Lookup(expr.Sel.Value)
 	}
 	panic(fmt.Sprintf("unexpected packname: %#v", expr))
 }
@@ -911,7 +911,7 @@
 	}
 
 	sym := p.packname(typ)
-	n := p.nodSym(typ, ODCLFIELD, oldname(sym), lookup(sym.Name))
+	n := p.nodSym(typ, ODCLFIELD, importName(sym), lookup(sym.Name))
 	n.SetEmbedded(true)
 
 	if isStar {
diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
index 9362c74..9c6cd24 100644
--- a/src/cmd/compile/internal/gc/subr.go
+++ b/src/cmd/compile/internal/gc/subr.go
@@ -271,13 +271,6 @@
 	return lookupN(prefix, int(n))
 }
 
-func restrictlookup(name string, pkg *types.Pkg) *types.Sym {
-	if !types.IsExported(name) && pkg != localpkg {
-		yyerror("cannot refer to unexported name %s.%s", pkg.Name, name)
-	}
-	return pkg.Lookup(name)
-}
-
 // find all the exported symbols in package opkg
 // and make them available in the current package
 func importdot(opkg *types.Pkg, pack *Node) {
diff --git a/test/fixedbugs/bug229.go b/test/fixedbugs/bug229.go
index 4baf65e..a30202f 100644
--- a/test/fixedbugs/bug229.go
+++ b/test/fixedbugs/bug229.go
@@ -10,11 +10,11 @@
 
 func main() {
 	var t testing.T
-	
+
 	// make sure error mentions that
 	// name is unexported, not just "name not found".
 
-	t.common.name = nil	// ERROR "unexported"
-	
-	println(testing.anyLowercaseName("asdf"))	// ERROR "unexported" "undefined: testing.anyLowercaseName"
+	t.common.name = nil // ERROR "unexported"
+
+	println(testing.anyLowercaseName("asdf")) // ERROR "unexported"
 }
diff --git a/test/fixedbugs/issue22921.go b/test/fixedbugs/issue22921.go
new file mode 100644
index 0000000..04f78b2
--- /dev/null
+++ b/test/fixedbugs/issue22921.go
@@ -0,0 +1,18 @@
+// errorcheck
+
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "bytes"
+
+type _ struct{ bytes.nonexist } // ERROR "unexported"
+
+type _ interface{ bytes.nonexist } // ERROR "unexported"
+
+func main() {
+	var _ bytes.Buffer
+	var _ bytes.buffer // ERROR "unexported"
+}
diff --git a/test/runtime.go b/test/runtime.go
index 0cf781b..bccc9b5 100644
--- a/test/runtime.go
+++ b/test/runtime.go
@@ -17,5 +17,5 @@
 import "runtime"
 
 func main() {
-	runtime.printbool(true)	// ERROR "unexported" "undefined"
+	runtime.printbool(true)	// ERROR "unexported"
 }