encoding/protobuf: implement time type conversions

Adheres to protobuf JSON mapping.

Also fixes a bug where a filed could mask the short
name of the import time packag.

Change-Id: Ib5e71023a4d68492e35da02e997c47f4e09e3dd5
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2722
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/encoding/protobuf/parse.go b/encoding/protobuf/parse.go
index 7f36f7f..8280f06 100644
--- a/encoding/protobuf/parse.go
+++ b/encoding/protobuf/parse.go
@@ -63,12 +63,12 @@
 	tfile.SetLinesForContent(b)
 
 	p = &protoConverter{
-		id:      filename,
-		state:   s,
-		tfile:   tfile,
-		used:    map[string]bool{},
-		symbols: map[string]bool{},
-		aliases: map[string]string{},
+		id:       filename,
+		state:    s,
+		tfile:    tfile,
+		imported: map[string]bool{},
+		symbols:  map[string]bool{},
+		aliases:  map[string]string{},
 	}
 
 	defer func() {
@@ -133,14 +133,14 @@
 		p.topElement(e)
 	}
 
-	used := []string{}
-	for k := range p.used {
-		used = append(used, k)
+	imported := []string{}
+	for k := range p.imported {
+		imported = append(imported, k)
 	}
-	sort.Strings(used)
-	p.sorted = used
+	sort.Strings(imported)
+	p.sorted = imported
 
-	for _, v := range used {
+	for _, v := range imported {
 		spec := &ast.ImportSpec{
 			Path: &ast.BasicLit{Kind: token.STRING, Value: strconv.Quote(v)},
 		}
@@ -172,8 +172,8 @@
 	file   *ast.File
 	inBody bool
 
-	sorted []string
-	used   map[string]bool
+	sorted   []string
+	imported map[string]bool
 
 	path    []string
 	scope   []map[string]mapping // for symbols resolution within package.
@@ -214,6 +214,15 @@
 		case *proto.Enum:
 			name = x.Name
 			pos = x.Position
+		case *proto.NormalField:
+			name = x.Name
+			pos = x.Position
+		case *proto.MapField:
+			name = x.Name
+			pos = x.Position
+		case *proto.Oneof:
+			name = x.Name
+			pos = x.Position
 		default:
 			continue
 		}
@@ -228,26 +237,25 @@
 }
 
 func (p *protoConverter) uniqueTop(name string) string {
-	if len(p.path) == 0 {
-		return name
-	}
 	a := strings.SplitN(name, ".", 2)
-	if p.path[len(p.path)-1] == a[0] {
-		first := a[0]
-		alias, ok := p.aliases[first]
-		if !ok {
-			// TODO: this is likely to be okay, but find something better.
-			alias = "__" + first
-			p.file.Decls = append(p.file.Decls, &ast.Alias{
-				Ident: ast.NewIdent(alias),
-				Expr:  ast.NewIdent(first),
-			})
-			p.aliases[first] = alias
+	for i := len(p.scope) - 1; i > 0; i-- {
+		if _, ok := p.scope[i][a[0]]; ok {
+			first := a[0]
+			alias, ok := p.aliases[first]
+			if !ok {
+				// TODO: this is likely to be okay, but find something better.
+				alias = "__" + first
+				p.file.Decls = append(p.file.Decls, &ast.Alias{
+					Ident: ast.NewIdent(alias),
+					Expr:  ast.NewIdent(first),
+				})
+				p.aliases[first] = alias
+			}
+			if len(a) > 1 {
+				alias += "." + a[1]
+			}
+			return alias
 		}
-		if len(a) > 1 {
-			alias += "." + a[1]
-		}
-		return alias
 	}
 	return name
 }
@@ -265,6 +273,9 @@
 }
 
 func (p *protoConverter) resolve(pos scanner.Position, name string, options []*proto.Option) string {
+	if s, ok := protoToCUE(name, options); ok {
+		return s
+	}
 	if strings.HasPrefix(name, ".") {
 		return p.resolveTopScope(pos, name[1:], options)
 	}
@@ -286,16 +297,13 @@
 		}
 		if m, ok := p.scope[0][name[:i]]; ok {
 			if m.pkg != nil {
-				p.used[m.pkg.goPkgPath] = true
+				p.imported[m.pkg.goPkgPath] = true
 				// TODO: do something more principled.
 			}
 			cueName := strings.Replace(name[i:], ".", "_", -1)
 			return p.uniqueTop(m.ref + cueName)
 		}
 	}
-	if s, ok := protoToCUE(name, options); ok {
-		return s
-	}
 	failf(pos, "name %q not found", name)
 	return ""
 }