//ranges through config file and checks all expressions. // prints result messages to stdout func (c *checker) CheckAll() ([]CheckResult, error) { result := []CheckResult{} cnf, err := conf.ReadConfigFile(c.configFile) if err != nil { return nil, err } for _, section := range cnf.GetSections() { if section == "default" { continue } expr, _ := cnf.GetString(section, "expr") _, r, err := types.Eval(expr, c.pkg, c.sc) if err != nil { fmt.Fprintln(os.Stderr, err) continue } cr := &CheckResult{ Name: section, } var m string if exact.BoolVal(r) { m, err = cnf.GetString(section, "true") if err != nil { continue } } else { m, err = cnf.GetString(section, "false") if err != nil { continue } } val, err := cnf.GetString(section, "val") if err == nil { t, v, err := types.Eval(val, c.pkg, c.sc) if err == nil { if types.Identical(t, types.Typ[types.UntypedFloat]) || types.Identical(t, types.Typ[types.Float64]) { x, _ := exact.Float64Val(v) cr.Value = x } } } owner, err := cnf.GetString(section, "owner") if err == nil { cr.Owner = owner } else { cr.Owner = "unknown" } _, msg, err := types.Eval(m, c.pkg, c.sc) if err != nil { cr.Message = m } else { cr.Message = exact.StringVal(msg) } result = append(result, *cr) } return result, nil }
func (c *FunctionCache) NamedFunction(name string, signature string) llvm.Value { f, _ := c.functions[name+":"+signature] if !f.IsNil() { return f } if strings.HasPrefix(name, c.module.Name+".") { obj := c.pkg.Scope().Lookup(name[len(c.module.Name)+1:]) if obj == nil { panic("Missing function: " + name) } value := c.Resolve(c.objectdata[obj].Ident) f = llvm.ConstExtractValue(value.LLVMValue(), []uint32{0}) } else { if c.runtimetypespkg == nil { // Parse the runtime package, since we may need to refer to // its types. buildpkg, err := build.Import("github.com/axw/llgo/pkg/runtime", "", 0) if err != nil { panic(err) } // All types visible to the compiler are in "types.go". runtimefiles := []string{path.Join(buildpkg.Dir, "types.go")} fset := token.NewFileSet() files, err := parseFiles(fset, runtimefiles) if err != nil { panic(err) } c.runtimetypespkg, err = c.typecheck("runtime", fset, files) if err != nil { panic(err) } } pkg := c.runtimetypespkg scope := pkg.Scope().Child(0) ftype, _, err := types.Eval(signature+"{panic()}", pkg, scope) if err != nil { panic(err) } llvmfntyp := c.types.ToLLVM(ftype).StructElementTypes()[0].ElementType() f = llvm.AddFunction(c.module.Module, name, llvmfntyp) } c.functions[name+":"+signature] = f return f }
func (p *Package) Eval(name string) (result Type, err error) { t, _, typesErr := types.Eval(name, p.Package, p.Scope()) if typesErr != nil { err = typesErr return } result = Type{ Package: p, Pointer: isPointer(t), Name: strings.TrimLeft(name, Pointer(true).String()), // trims the * if it exists comparable: isComparable(t), numeric: isNumeric(t), ordered: isOrdered(t), Type: t, } return }
func TestExportedType(t *testing.T) { tests := []struct { typString string exp bool }{ {"int", true}, {"string", false}, // references the shadowed builtin "string" {"T", true}, {"t", false}, {"*T", true}, {"*t", false}, {"map[int]complex128", true}, } for _, test := range tests { src := `package foo; type T int; type t int; type string struct{}` fset := token.NewFileSet() file, err := parser.ParseFile(fset, "foo.go", src, 0) if err != nil { t.Fatalf("Parsing %q: %v", src, err) } // use the package name as package path pkg, err := types.Check(file.Name.Name, fset, []*ast.File{file}) if err != nil { t.Fatalf("Type checking %q: %v", src, err) } // Use the first child scope of the package, which will be the file scope. scope := pkg.Scope().Child(0) typ, _, err := types.Eval(test.typString, pkg, scope) if err != nil { t.Errorf("types.Eval(%q): %v", test.typString, err) continue } if got := exportedType(typ); got != test.exp { t.Errorf("exportedType(%v) = %t, want %t", typ, got, test.exp) } } }
// Returns one gen Package per Go package found in current directory func getPackages() (result []*Package) { fset := token.NewFileSet() rootDir := "./" astPackages, err := parser.ParseDir(fset, rootDir, nil, parser.ParseComments) if err != nil { errs = append(errs, err) } for name, astPackage := range astPackages { pkg := &Package{Name: name} typesPkg, err := types.Check(name, fset, getAstFiles(astPackage, rootDir)) if err != nil { errs = append(errs, err) } // fall back to Universe scope if types.Check fails; "best-effort" to handle primitives, at least scope := types.Universe if typesPkg != nil { scope = typesPkg.Scope() } docPkg := doc.New(astPackage, name, doc.AllDecls) for _, docType := range docPkg.Types { // look for deprecated struct tags, used for 'custom methods' in older version of gen if t, _, err := types.Eval(docType.Name, typesPkg, scope); err == nil { checkDeprecatedTags(t) } // identify marked-up types spec, found := getGenSpec(docType.Doc, docType.Name) if !found { continue } typ := &Type{ Package: pkg, Pointer: spec.Pointer, Name: docType.Name, } standardMethods, projectionMethods, err := determineMethods(spec) if err != nil { errs = append(errs, err) } // assemble standard methods with type verification t, _, err := types.Eval(typ.LocalName(), typesPkg, scope) known := err == nil if !known { addError(fmt.Sprintf("failed to evaluate type %s (%s)", typ.Name, err)) } if known { numeric := isNumeric(t) comparable := isComparable(t) ordered := isOrdered(t) for _, s := range standardMethods { st, ok := standardTemplates[s] if !ok { addError(fmt.Sprintf("unknown standard method %s", s)) } valid := (!st.RequiresNumeric || numeric) && (!st.RequiresComparable || comparable) && (!st.RequiresOrdered || ordered) if valid { typ.StandardMethods = append(typ.StandardMethods, s) } } } // assemble projections with type verification if spec.Projections != nil { if spec.Projections.Negated { addError(fmt.Sprintf("negation of projected types (see projection tag on %s) is unsupported", docType.Name)) } for _, s := range spec.Projections.Items { numeric := false comparable := true // sensible default? ordered := false t, _, err := types.Eval(s, typesPkg, scope) known := err == nil if !known { addError(fmt.Sprintf("unable to identify type %s, projected on %s (%s)", s, docType.Name, err)) } else { numeric = isNumeric(t) comparable = isComparable(t) ordered = isOrdered(t) } for _, m := range projectionMethods { pt, ok := projectionTemplates[m] if !ok { addError(fmt.Sprintf("unknown projection method %v", m)) continue } valid := (!pt.RequiresNumeric || numeric) && (!pt.RequiresComparable || comparable) && (!pt.RequiresOrdered || ordered) if valid { typ.AddProjection(m, s) } } } } if spec.Containers != nil { if spec.Containers.Negated { addError(fmt.Sprintf("negation of containers (see containers tag on %s) is unsupported", docType.Name)) } typ.Containers = spec.Containers.Items } determineImports(typ) pkg.Types = append(pkg.Types, typ) } // only add it to the results if there is something there if len(pkg.Types) > 0 { result = append(result, pkg) } } return }
func getTypes(directive string, filter func(os.FileInfo) bool) ([]Type, error) { typs := make([]Type, 0) fset := token.NewFileSet() rootDir := "./" astPackages, astErr := parser.ParseDir(fset, rootDir, filter, parser.ParseComments) if astErr != nil { return typs, astErr } for name, astPackage := range astPackages { astFiles, astErr := getAstFiles(astPackage, rootDir) if astErr != nil { return typs, astErr } typesPkg, typesErr := types.Check(name, fset, astFiles) if typesErr != nil { return typs, typesErr } pkg := &Package{typesPkg} // doc package is handy for pulling types and their comments docPkg := doc.New(astPackage, name, doc.AllDecls) for _, docType := range docPkg.Types { pointer, tags, found, tagErr := parseTags(directive, docType.Doc) if tagErr != nil { return typs, tagErr } if !found { continue } typ := Type{ Package: pkg, Pointer: pointer, Name: docType.Name, Tags: tags, } t, _, err := types.Eval(typ.LocalName(), typesPkg, typesPkg.Scope()) known := err == nil if !known { err = errors.New(fmt.Sprintf("failed to evaluate type %s (%s)", typ.Name, err)) continue } typ.comparable = isComparable(t) typ.numeric = isNumeric(t) typ.ordered = isOrdered(t) typ.Type = t typs = append(typs, typ) } } return typs, nil }