func LoadPackage(dir string) (*types.Package, error) { fset := token.NewFileSet() packages, err := parser.ParseDir(fset, dir, ok, 0) if err != nil { return nil, err } var pkg *ast.Package if len(packages) == 0 { return nil, errors.New("no packages found in given directory") } for _, astPkg := range packages { pkg = astPkg break } fileset := make([]*ast.File, len(pkg.Files)) idx := 0 for _, file := range pkg.Files { fileset[idx] = file idx++ } typesPkg, err := types.Check(dir, fset, fileset) if err != nil { return nil, err } return typesPkg, nil }
func pkgForSource(src string) (*types.Package, error) { // parse file fset := token.NewFileSet() f, err := parser.ParseFile(fset, "", src, 0) if err != nil { return nil, err } // typecheck file return types.Check("import-test", fset, []*ast.File{f}) }
func (c *checker) NewScopeAndPackage() error { fset := token.NewFileSet() src := `package p` f, err := parser.ParseFile(fset, "p", src, 0) if err != nil { return err } //initialize package and scope to evaluate expressions c.pkg, err = types.Check("main", fset, []*ast.File{f}) if err != nil { return err } c.sc = c.pkg.Scope() return nil }
func getPackage(fset *token.FileSet, a *ast.Package) (*Package, error) { // pull map into a slice var files []*ast.File for _, f := range a.Files { files = append(files, f) } typesPkg, err := types.Check(a.Name, fset, files) if err != nil { return nil, err } return &Package{typesPkg}, nil }
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 }
func (w *World) CompilePackage(fset *token.FileSet, files []*ast.File, pkgpath string) (Code, error) { pkgFiles := make(map[string]*ast.File) for _, f := range files { pkgFiles[f.Name.Name] = f } //pkg, err := ast.NewPackage(fset, pkgFiles, srcImporter, types.Universe) pkg, err := types.Check(files[0].Name.String(), fset, files) if err != nil { return nil, err } if pkg == nil { return nil, errors.New("could not create an AST package out of ast.Files") } switch g_visiting[pkgpath] { case done: return &pkgCode{w, make(code, 0)}, nil case visiting: //fmt.Printf("** package dependency cycle **\n") return nil, errors.New("package dependency cycle") } g_visiting[pkgpath] = visiting // create a new scope in which to process this new package imports := []*ast.ImportSpec{} for _, f := range files { imports = append(imports, f.Imports...) } for _, imp := range imports { path, _ := strconv.Unquote(imp.Path.Value) if _, ok := universe.pkgs[path]; ok { // already compiled continue } imp_files, err := findPkgFiles(path) if err != nil { return nil, errors.New(fmt.Sprintf("could not find files for package [%s]", path)) } code, err := w.CompilePackage(fset, imp_files, path) if err != nil { return nil, err } _, err = code.Run() if err != nil { return nil, err } } prev_scope := w.scope w.scope = w.scope.ChildScope() w.scope.global = true defer func() { g_visiting[pkgpath] = done // add this scope (the package's scope) to the lookup-table of packages universe.pkgs[pkgpath] = w.scope // restore the previous scope w.scope.exit() if pkgpath != "main" { w.scope = prev_scope } }() decls := make([]ast.Decl, 0) for _, f := range files { decls = append(decls, f.Decls...) } code, err := w.CompileDeclList(fset, decls) if err != nil { return nil, err } _, err = code.Run() if err != nil { return nil, err } //FIXME: make sure all the imports are used at this point. { // store the init function (if any) for later use init_code, init_err := w.Compile(fset, "init()") if init_code != nil { if init_err == nil || init_err != nil { w.inits = append(w.inits, init_code) } } } return code, err }