this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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í <mvdan@mvdan.cc>
Change-Id: Idb6edb844359a9936a719f42ed788e6704f961f8
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/557328
TryBot-Result: CUEcueckoo <cueckoo@cuelang.org>
Reviewed-by: Roger Peppe <rogpeppe@gmail.com>

+90 -123
+90 -123
pkg/gen.go
··· 28 28 _ "embed" 29 29 "flag" 30 30 "fmt" 31 - "go/ast" 32 31 "go/constant" 33 32 "go/format" 34 - "go/parser" 35 - "go/printer" 36 33 "go/token" 34 + "go/types" 37 35 "log" 38 36 "math/big" 39 37 "os" 40 38 "path" 41 39 "path/filepath" 40 + "sort" 42 41 "strings" 43 42 "text/template" 44 43 ··· 107 106 packagesList = append(packagesList, path.Join(pkgParent, pkg)) 108 107 } 109 108 110 - cfg := &packages.Config{Mode: packages.NeedName | packages.NeedFiles} 109 + cfg := &packages.Config{Mode: packages.NeedName | packages.NeedFiles | packages.NeedTypes} 111 110 pkgs, err := packages.Load(cfg, packagesList...) 112 111 if err != nil { 113 112 fmt.Fprintf(os.Stderr, "load: %v\n", err) ··· 127 126 dir string 128 127 w *bytes.Buffer 129 128 cuePkgPath string 130 - fset *token.FileSet 131 129 first bool 132 130 } 133 131 ··· 142 140 dir: pkgDir, 143 141 cuePkgPath: cuePkg, 144 142 w: &bytes.Buffer{}, 145 - fset: token.NewFileSet(), 146 143 } 147 144 148 145 params := headerParams{ ··· 175 172 if !skipRegister { 176 173 fmt.Fprintf(g.w, "var p = &pkg.Package{\nNative: []*pkg.Builtin{") 177 174 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 185 177 } 186 178 fmt.Fprintf(g.w, "},\n") 187 179 if err := g.processCUE(); err != nil { ··· 192 184 193 185 b, err := format.Source(g.w.Bytes()) 194 186 if err != nil { 187 + fmt.Printf("go/format error on %s: %v\n", pkg.PkgPath, err) 195 188 b = g.w.Bytes() // write the unformatted source 196 189 } 197 190 ··· 241 234 return nil 242 235 } 243 236 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 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 248 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 + }) 249 258 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: 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) 278 + default: 279 + fmt.Printf("Dropped entry %s.%s (%T: %v)\n", g.cuePkgPath, obj.Name(), v.Kind(), v.ExactString()) 267 280 continue 268 - default: 269 - panic(fmt.Errorf("gen %s: unexpected spec of type %s", filename, x.Tok)) 270 281 } 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) 273 286 } 274 287 } 275 288 return nil 276 289 } 277 290 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 - } 291 + var errorType = types.Universe.Lookup("error").Type() 298 292 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 - } 311 - 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 { 314 296 return 315 297 } 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) 333 302 return 334 303 } 335 304 ··· 337 306 fmt.Fprintf(g.w, "{\n") 338 307 defer fmt.Fprintf(g.w, "}") 339 308 340 - fmt.Fprintf(g.w, "Name: %q,\n", x.Name.Name) 309 + fmt.Fprintf(g.w, "Name: %q,\n", fn.Name()) 341 310 342 311 args := []string{} 343 312 vals := []string{} 344 313 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) 353 321 } 354 322 355 323 fmt.Fprintf(g.w, "Params: []pkg.Param{\n") ··· 358 326 } 359 327 fmt.Fprintf(g.w, "\n},\n") 360 328 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())) 363 330 364 331 argList := strings.Join(args, ", ") 365 332 valList := strings.Join(vals, ", ") ··· 376 343 } 377 344 fmt.Fprintln(g.w, "if c.Do() {") 378 345 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) 381 348 } 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) 383 350 } 384 351 } 385 352 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() 389 358 } 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": 394 361 return "bigInt" 395 - case "big.Float": 362 + case "math/big.Float": 396 363 return "bigFloat" 397 - case "big.Rat": 364 + case "math/big.Rat": 398 365 return "bigRat" 399 - case "adt.Bottom": 366 + case "cuelang.org/go/internal/core/adt.Bottom": 400 367 return "error" 401 - case "internal.Decimal": 368 + case "github.com/cockroachdb/apd/v3.Decimal": 402 369 return "decimal" 403 - case "pkg.List": 370 + case "cuelang.org/go/internal/pkg.List": 404 371 return "cueList" 405 - case "pkg.Struct": 372 + case "cuelang.org/go/internal/pkg.Struct": 406 373 return "struct" 407 - case "[]*internal.Decimal": 374 + case "[]*github.com/cockroachdb/apd/v3.Decimal": 408 375 return "decimalList" 409 - case "cue.Value": 376 + case "cuelang.org/go/cue.Value": 410 377 return "value" 411 - case "cue.List": 378 + case "cuelang.org/go/cue.List": 412 379 return "list" 413 380 case "[]string": 414 381 return "stringList" 415 382 case "[]byte": 416 383 return "bytes" 417 - case "[]cue.Value": 384 + case "[]cuelang.org/go/cue.Value": 418 385 return "list" 419 386 case "io.Reader": 420 387 return "reader" ··· 425 392 } 426 393 } 427 394 428 - func (g *generator) goToCUE(expr ast.Expr) (cueKind string) { 395 + func (g *generator) goToCUE(typ types.Type) (cueKind string) { 429 396 // TODO: detect list and structs types for return values. 430 - switch k := g.goKind(expr); k { 397 + switch k := g.goKind(typ); k { 431 398 case "error": 432 399 cueKind += "adt.BottomKind" 433 400 case "bool":