Beispiel #1
0
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
}
Beispiel #2
0
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})
}
Beispiel #3
0
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
}
Beispiel #4
0
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
}
Beispiel #5
0
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)
		}
	}
}
Beispiel #6
0
// 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
}
Beispiel #7
0
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
}
Beispiel #8
0
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
}