Skip to content

Commit 2091170

Browse files
committed
pkg: teach gen.go about go/types
This cuts about 30 lines of code, since logic like toValue is obsolete. We also no longer need to deal with irrelevant bits of the syntax tree. I'm leaving a TODO about polishing up the use of strings in the future. For now, this is enough to unblock the use of more complex constants, since the previous code with toValue was too simple. Signed-off-by: Daniel Martí <[email protected]> Change-Id: Idb6edb844359a9936a719f42ed788e6704f961f8 Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/557328 TryBot-Result: CUEcueckoo <[email protected]> Reviewed-by: Roger Peppe <[email protected]>
1 parent e9a412d commit 2091170

File tree

1 file changed

+91
-124
lines changed

1 file changed

+91
-124
lines changed

pkg/gen.go

+91-124
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,16 @@ import (
2828
_ "embed"
2929
"flag"
3030
"fmt"
31-
"go/ast"
3231
"go/constant"
3332
"go/format"
34-
"go/parser"
35-
"go/printer"
3633
"go/token"
34+
"go/types"
3735
"log"
3836
"math/big"
3937
"os"
4038
"path"
4139
"path/filepath"
40+
"sort"
4241
"strings"
4342
"text/template"
4443

@@ -107,7 +106,7 @@ func main() {
107106
packagesList = append(packagesList, path.Join(pkgParent, pkg))
108107
}
109108

110-
cfg := &packages.Config{Mode: packages.NeedName | packages.NeedFiles}
109+
cfg := &packages.Config{Mode: packages.NeedName | packages.NeedFiles | packages.NeedTypes}
111110
pkgs, err := packages.Load(cfg, packagesList...)
112111
if err != nil {
113112
fmt.Fprintf(os.Stderr, "load: %v\n", err)
@@ -127,7 +126,6 @@ type generator struct {
127126
dir string
128127
w *bytes.Buffer
129128
cuePkgPath string
130-
fset *token.FileSet
131129
first bool
132130
}
133131

@@ -142,7 +140,6 @@ func generate(pkg *packages.Package) error {
142140
dir: pkgDir,
143141
cuePkgPath: cuePkg,
144142
w: &bytes.Buffer{},
145-
fset: token.NewFileSet(),
146143
}
147144

148145
params := headerParams{
@@ -175,13 +172,8 @@ func generate(pkg *packages.Package) error {
175172
if !skipRegister {
176173
fmt.Fprintf(g.w, "var p = &pkg.Package{\nNative: []*pkg.Builtin{")
177174
g.first = true
178-
for _, filename := range pkg.GoFiles {
179-
if filename == genFile {
180-
continue
181-
}
182-
if err := g.processGo(filename); err != nil {
183-
return err
184-
}
175+
if err := g.processGo(pkg); err != nil {
176+
return err
185177
}
186178
fmt.Fprintf(g.w, "},\n")
187179
if err := g.processCUE(); err != nil {
@@ -192,6 +184,7 @@ func generate(pkg *packages.Package) error {
192184

193185
b, err := format.Source(g.w.Bytes())
194186
if err != nil {
187+
fmt.Printf("go/format error on %s: %v\n", pkg.PkgPath, err)
195188
b = g.w.Bytes() // write the unformatted source
196189
}
197190

@@ -241,115 +234,90 @@ func (g *generator) processCUE() error {
241234
return nil
242235
}
243236

244-
func (g *generator) processGo(filename string) error {
245-
f, err := parser.ParseFile(g.fset, filename, nil, parser.ParseComments)
246-
if err != nil {
247-
return err
248-
}
237+
func (g *generator) processGo(pkg *packages.Package) error {
238+
// We sort the objects by their original source code position.
239+
// Otherwise go/types defaults to sorting by name strings.
240+
// We could remove this code if we were fine with sorting by name.
241+
scope := pkg.Types.Scope()
242+
type objWithPos struct {
243+
obj types.Object
244+
pos token.Position
245+
}
246+
var objs []objWithPos
247+
for _, name := range scope.Names() {
248+
obj := scope.Lookup(name)
249+
objs = append(objs, objWithPos{obj, pkg.Fset.Position(obj.Pos())})
250+
}
251+
sort.Slice(objs, func(i, j int) bool {
252+
obj1, obj2 := objs[i], objs[j]
253+
if obj1.pos.Filename == obj2.pos.Filename {
254+
return obj1.pos.Line < obj2.pos.Line
255+
}
256+
return obj1.pos.Filename < obj2.pos.Filename
257+
})
249258

250-
for _, d := range f.Decls {
251-
switch x := d.(type) {
252-
case *ast.GenDecl:
253-
switch x.Tok {
254-
case token.CONST:
255-
for _, spec := range x.Specs {
256-
spec := spec.(*ast.ValueSpec)
257-
if ast.IsExported(spec.Names[0].Name) {
258-
g.genConst(spec)
259-
}
260-
}
261-
case token.VAR:
262-
continue
263-
case token.TYPE:
264-
// TODO: support type declarations.
265-
continue
266-
case token.IMPORT:
267-
continue
259+
for _, obj := range objs {
260+
obj := obj.obj // no longer need the token.Position
261+
if !obj.Exported() {
262+
continue
263+
}
264+
// TODO: support type declarations.
265+
switch obj := obj.(type) {
266+
case *types.Const:
267+
var value string
268+
switch v := obj.Val(); v.Kind() {
269+
case constant.Bool, constant.Int, constant.String:
270+
// TODO: convert octal numbers
271+
value = v.ExactString()
272+
case constant.Float:
273+
var rat big.Rat
274+
rat.SetString(v.ExactString())
275+
var float big.Float
276+
float.SetRat(&rat)
277+
value = float.Text('g', -1)
268278
default:
269-
panic(fmt.Errorf("gen %s: unexpected spec of type %s", filename, x.Tok))
279+
fmt.Printf("Dropped entry %s.%s (%T: %v)\n", g.cuePkgPath, obj.Name(), v.Kind(), v.ExactString())
280+
continue
270281
}
271-
case *ast.FuncDecl:
272-
g.genFunc(x)
282+
g.sep()
283+
fmt.Fprintf(g.w, "{\nName: %q,\n Const: %q,\n}", obj.Name(), value)
284+
case *types.Func:
285+
g.genFunc(obj)
273286
}
274287
}
275288
return nil
276289
}
277290

278-
func (g *generator) genConst(spec *ast.ValueSpec) {
279-
name := spec.Names[0].Name
280-
value := ""
281-
switch v := g.toValue(spec.Values[0]); v.Kind() {
282-
case constant.Bool, constant.Int, constant.String:
283-
// TODO: convert octal numbers
284-
value = v.ExactString()
285-
case constant.Float:
286-
var rat big.Rat
287-
rat.SetString(v.ExactString())
288-
var float big.Float
289-
float.SetRat(&rat)
290-
value = float.Text('g', -1)
291-
default:
292-
fmt.Printf("Dropped entry %s.%s (%T: %v)\n", g.cuePkgPath, name, v.Kind(), v.ExactString())
293-
return
294-
}
295-
g.sep()
296-
fmt.Fprintf(g.w, "{\nName: %q,\n Const: %q,\n}", name, value)
297-
}
298-
299-
func (g *generator) toValue(x ast.Expr) constant.Value {
300-
switch x := x.(type) {
301-
case *ast.BasicLit:
302-
return constant.MakeFromLiteral(x.Value, x.Kind, 0)
303-
case *ast.BinaryExpr:
304-
return constant.BinaryOp(g.toValue(x.X), x.Op, g.toValue(x.Y))
305-
case *ast.UnaryExpr:
306-
return constant.UnaryOp(x.Op, g.toValue(x.X), 0)
307-
default:
308-
panic(fmt.Errorf("%s: unsupported expression type %T: %#v", g.cuePkgPath, x, x))
309-
}
310-
}
291+
var errorType = types.Universe.Lookup("error").Type()
311292

312-
func (g *generator) genFunc(x *ast.FuncDecl) {
313-
if x.Body == nil || !ast.IsExported(x.Name.Name) {
293+
func (g *generator) genFunc(fn *types.Func) {
294+
sign := fn.Type().(*types.Signature)
295+
if sign.Recv() != nil {
314296
return
315297
}
316-
types := []string{}
317-
if x.Type.Results != nil {
318-
for _, f := range x.Type.Results.List {
319-
if len(f.Names) > 0 {
320-
for range f.Names {
321-
types = append(types, g.goKind(f.Type))
322-
}
323-
} else {
324-
types = append(types, g.goKind(f.Type))
325-
}
326-
}
327-
}
328-
if x.Recv != nil {
329-
return
330-
}
331-
if n := len(types); n != 1 && (n != 2 || types[1] != "error") {
332-
fmt.Printf("Dropped func %s.%s: must have one return value or a value and an error %v\n", g.cuePkgPath, x.Name.Name, types)
298+
params := sign.Params()
299+
results := sign.Results()
300+
if results == nil || (results.Len() != 1 && results.At(1).Type() != errorType) {
301+
fmt.Printf("Dropped func %s.%s: must have one return value or a value and an error %v\n", g.cuePkgPath, fn.Name(), sign)
333302
return
334303
}
335304

336305
g.sep()
337306
fmt.Fprintf(g.w, "{\n")
338307
defer fmt.Fprintf(g.w, "}")
339308

340-
fmt.Fprintf(g.w, "Name: %q,\n", x.Name.Name)
309+
fmt.Fprintf(g.w, "Name: %q,\n", fn.Name())
341310

342311
args := []string{}
343312
vals := []string{}
344313
kind := []string{}
345-
for _, f := range x.Type.Params.List {
346-
for _, name := range f.Names {
347-
typ := strings.Title(g.goKind(f.Type))
348-
argKind := g.goToCUE(f.Type)
349-
vals = append(vals, fmt.Sprintf("c.%s(%d)", typ, len(args)))
350-
args = append(args, name.Name)
351-
kind = append(kind, argKind)
352-
}
314+
for i := 0; i < params.Len(); i++ {
315+
param := params.At(i)
316+
typ := strings.Title(g.goKind(param.Type()))
317+
argKind := g.goToCUE(param.Type())
318+
vals = append(vals, fmt.Sprintf("c.%s(%d)", typ, len(args)))
319+
args = append(args, param.Name())
320+
kind = append(kind, argKind)
353321
}
354322

355323
fmt.Fprintf(g.w, "Params: []pkg.Param{\n")
@@ -358,8 +326,7 @@ func (g *generator) genFunc(x *ast.FuncDecl) {
358326
}
359327
fmt.Fprintf(g.w, "\n},\n")
360328

361-
expr := x.Type.Results.List[0].Type
362-
fmt.Fprintf(g.w, "Result: %s,\n", g.goToCUE(expr))
329+
fmt.Fprintf(g.w, "Result: %s,\n", g.goToCUE(results.At(0).Type()))
363330

364331
argList := strings.Join(args, ", ")
365332
valList := strings.Join(vals, ", ")
@@ -376,45 +343,45 @@ func (g *generator) genFunc(x *ast.FuncDecl) {
376343
}
377344
fmt.Fprintln(g.w, "if c.Do() {")
378345
defer fmt.Fprintln(g.w, "}")
379-
if len(types) == 1 {
380-
fmt.Fprintf(g.w, "c.Ret = %s(%s)", x.Name.Name, argList)
346+
if results.Len() == 1 {
347+
fmt.Fprintf(g.w, "c.Ret = %s(%s)", fn.Name(), argList)
381348
} else {
382-
fmt.Fprintf(g.w, "c.Ret, c.Err = %s(%s)", x.Name.Name, argList)
349+
fmt.Fprintf(g.w, "c.Ret, c.Err = %s(%s)", fn.Name(), argList)
383350
}
384351
}
385352

386-
func (g *generator) goKind(expr ast.Expr) string {
387-
if star, isStar := expr.(*ast.StarExpr); isStar {
388-
expr = star.X
353+
// TODO(mvdan): goKind and goToCUE still use a lot of strings; simplify.
354+
355+
func (g *generator) goKind(typ types.Type) string {
356+
if ptr, ok := typ.(*types.Pointer); ok {
357+
typ = ptr.Elem()
389358
}
390-
w := &bytes.Buffer{}
391-
printer.Fprint(w, g.fset, expr)
392-
switch str := w.String(); str {
393-
case "big.Int":
359+
switch str := types.TypeString(typ, nil); str {
360+
case "math/big.Int":
394361
return "bigInt"
395-
case "big.Float":
362+
case "math/big.Float":
396363
return "bigFloat"
397-
case "big.Rat":
364+
case "math/big.Rat":
398365
return "bigRat"
399-
case "adt.Bottom":
366+
case "cuelang.org/go/internal/core/adt.Bottom":
400367
return "error"
401-
case "internal.Decimal":
368+
case "github.com/cockroachdb/apd/v3.Decimal":
402369
return "decimal"
403-
case "pkg.List":
370+
case "cuelang.org/go/internal/pkg.List":
404371
return "cueList"
405-
case "pkg.Struct":
372+
case "cuelang.org/go/internal/pkg.Struct":
406373
return "struct"
407-
case "[]*internal.Decimal":
374+
case "[]*github.com/cockroachdb/apd/v3.Decimal":
408375
return "decimalList"
409-
case "cue.Value":
376+
case "cuelang.org/go/cue.Value":
410377
return "value"
411-
case "cue.List":
378+
case "cuelang.org/go/cue.List":
412379
return "list"
413380
case "[]string":
414381
return "stringList"
415382
case "[]byte":
416383
return "bytes"
417-
case "[]cue.Value":
384+
case "[]cuelang.org/go/cue.Value":
418385
return "list"
419386
case "io.Reader":
420387
return "reader"
@@ -425,9 +392,9 @@ func (g *generator) goKind(expr ast.Expr) string {
425392
}
426393
}
427394

428-
func (g *generator) goToCUE(expr ast.Expr) (cueKind string) {
395+
func (g *generator) goToCUE(typ types.Type) (cueKind string) {
429396
// TODO: detect list and structs types for return values.
430-
switch k := g.goKind(expr); k {
397+
switch k := g.goKind(typ); k {
431398
case "error":
432399
cueKind += "adt.BottomKind"
433400
case "bool":

0 commit comments

Comments
 (0)