Example #1
0
func packageFromInfo(prog *loader.Program, pkgInfo *loader.PackageInfo) *Package {
	files := map[string]*ast.File{}
	for _, f := range pkgInfo.Files {
		files[prog.Fset.File(f.Pos()).Name()] = f
	}

	// Ignore (perhaps) "unresolved identifier" errors
	astPkg, _ := ast.NewPackage(prog.Fset, files, nil, nil)

	var mode doc.Mode
	docPkg := doc.New(astPkg, pkgInfo.String(), mode)

	return NewPackage(prog.Fset, docPkg, pkgInfo.Pkg)
}
Example #2
0
func findSourceFile(pkg *loader.PackageInfo, typeName string) (*ast.File, *ast.GenDecl, *ast.TypeSpec, error) {
	for _, file := range pkg.Files {
		for _, decl := range file.Decls {
			if gd, ok := decl.(*ast.GenDecl); ok {
				for _, gs := range gd.Specs {
					if ts, ok := gs.(*ast.TypeSpec); ok {
						strfmtNme, isStrfmt := strfmtName(gd.Doc)
						if (isStrfmt && strfmtNme == typeName) || ts.Name != nil && ts.Name.Name == typeName {
							return file, gd, ts, nil
						}
					}
				}
			}
		}
	}
	return nil, nil, nil, fmt.Errorf("unable to find %s in %s", typeName, pkg.String())
}
Example #3
0
// processAnnotations takes an *ssa.Package and a
// *importer.PackageInfo, and processes all of the
// llgo source annotations attached to each top-level
// function and global variable.
func (c *compiler) processAnnotations(u *unit, pkginfo *loader.PackageInfo) {
	members := make(map[types.Object]llvm.Value, len(u.globals))
	for k, v := range u.globals {
		members[k.(ssa.Member).Object()] = v
	}
	applyAttributes := func(attrs []Attribute, idents ...*ast.Ident) {
		if len(attrs) == 0 {
			return
		}
		for _, ident := range idents {
			if v := members[pkginfo.ObjectOf(ident)]; !v.IsNil() {
				for _, attr := range attrs {
					attr.Apply(v)
				}
			}
		}
	}
	for _, f := range pkginfo.Files {
		for _, decl := range f.Decls {
			switch decl := decl.(type) {
			case *ast.FuncDecl:
				attrs := parseAttributes(decl.Doc)
				applyAttributes(attrs, decl.Name)
			case *ast.GenDecl:
				if decl.Tok != token.VAR {
					continue
				}
				for _, spec := range decl.Specs {
					varspec := spec.(*ast.ValueSpec)
					attrs := parseAttributes(decl.Doc)
					applyAttributes(attrs, varspec.Names...)
				}
			}
		}
	}
}
Example #4
0
func clearInfoFields(info *loader.PackageInfo) {
	// TODO(adonovan): opt: save memory by eliminating unneeded scopes/objects.
	// (Requires go/types change for Go 1.7.)
	//   info.Pkg.Scope().ClearChildren()

	// Discard the file ASTs and their accumulated type
	// information to save memory.
	info.Files = nil
	info.Defs = make(map[*ast.Ident]types.Object)
	info.Uses = make(map[*ast.Ident]types.Object)
	info.Implicits = make(map[ast.Node]types.Object)

	// Also, disable future collection of wholly unneeded
	// type information for the package in case there is
	// more type-checking to do (augmentation).
	info.Types = nil
	info.Scopes = nil
	info.Selections = nil
}
Example #5
0
// findInterestingNode classifies the syntax node denoted by path as one of:
//    - an expression, part of an expression or a reference to a constant
//      or variable;
//    - a type, part of a type, or a reference to a named type;
//    - a statement, part of a statement, or a label referring to a statement;
//    - part of a package declaration or import spec.
//    - none of the above.
// and returns the most "interesting" associated node, which may be
// the same node, an ancestor or a descendent.
//
func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.Node, action) {
	// TODO(adonovan): integrate with go/types/stdlib_test.go and
	// apply this to every AST node we can find to make sure it
	// doesn't crash.

	// TODO(adonovan): audit for ParenExpr safety, esp. since we
	// traverse up and down.

	// TODO(adonovan): if the users selects the "." in
	// "fmt.Fprintf()", they'll get an ambiguous selection error;
	// we won't even reach here.  Can we do better?

	// TODO(adonovan): describing a field within 'type T struct {...}'
	// describes the (anonymous) struct type and concludes "no methods".
	// We should ascend to the enclosing type decl, if any.

	for len(path) > 0 {
		switch n := path[0].(type) {
		case *ast.GenDecl:
			if len(n.Specs) == 1 {
				// Descend to sole {Import,Type,Value}Spec child.
				path = append([]ast.Node{n.Specs[0]}, path...)
				continue
			}
			return path, actionUnknown // uninteresting

		case *ast.FuncDecl:
			// Descend to function name.
			path = append([]ast.Node{n.Name}, path...)
			continue

		case *ast.ImportSpec:
			return path, actionPackage

		case *ast.ValueSpec:
			if len(n.Names) == 1 {
				// Descend to sole Ident child.
				path = append([]ast.Node{n.Names[0]}, path...)
				continue
			}
			return path, actionUnknown // uninteresting

		case *ast.TypeSpec:
			// Descend to type name.
			path = append([]ast.Node{n.Name}, path...)
			continue

		case ast.Stmt:
			return path, actionStmt

		case *ast.ArrayType,
			*ast.StructType,
			*ast.FuncType,
			*ast.InterfaceType,
			*ast.MapType,
			*ast.ChanType:
			return path, actionType

		case *ast.Comment, *ast.CommentGroup, *ast.File, *ast.KeyValueExpr, *ast.CommClause:
			return path, actionUnknown // uninteresting

		case *ast.Ellipsis:
			// Continue to enclosing node.
			// e.g. [...]T in ArrayType
			//      f(x...) in CallExpr
			//      f(x...T) in FuncType

		case *ast.Field:
			// TODO(adonovan): this needs more thought,
			// since fields can be so many things.
			if len(n.Names) == 1 {
				// Descend to sole Ident child.
				path = append([]ast.Node{n.Names[0]}, path...)
				continue
			}
			// Zero names (e.g. anon field in struct)
			// or multiple field or param names:
			// continue to enclosing field list.

		case *ast.FieldList:
			// Continue to enclosing node:
			// {Struct,Func,Interface}Type or FuncDecl.

		case *ast.BasicLit:
			if _, ok := path[1].(*ast.ImportSpec); ok {
				return path[1:], actionPackage
			}
			return path, actionExpr

		case *ast.SelectorExpr:
			// TODO(adonovan): use Selections info directly.
			if pkginfo.Uses[n.Sel] == nil {
				// TODO(adonovan): is this reachable?
				return path, actionUnknown
			}
			// Descend to .Sel child.
			path = append([]ast.Node{n.Sel}, path...)
			continue

		case *ast.Ident:
			switch pkginfo.ObjectOf(n).(type) {
			case *types.PkgName:
				return path, actionPackage

			case *types.Const:
				return path, actionExpr

			case *types.Label:
				return path, actionStmt

			case *types.TypeName:
				return path, actionType

			case *types.Var:
				// For x in 'struct {x T}', return struct type, for now.
				if _, ok := path[1].(*ast.Field); ok {
					_ = path[2].(*ast.FieldList) // assertion
					if _, ok := path[3].(*ast.StructType); ok {
						return path[3:], actionType
					}
				}
				return path, actionExpr

			case *types.Func:
				return path, actionExpr

			case *types.Builtin:
				// For reference to built-in function, return enclosing call.
				path = path[1:] // ascend to enclosing function call
				continue

			case *types.Nil:
				return path, actionExpr
			}

			// No object.
			switch path[1].(type) {
			case *ast.SelectorExpr:
				// Return enclosing selector expression.
				return path[1:], actionExpr

			case *ast.Field:
				// TODO(adonovan): test this.
				// e.g. all f in:
				//  struct { f, g int }
				//  interface { f() }
				//  func (f T) method(f, g int) (f, g bool)
				//
				// switch path[3].(type) {
				// case *ast.FuncDecl:
				// case *ast.StructType:
				// case *ast.InterfaceType:
				// }
				//
				// return path[1:], actionExpr
				//
				// Unclear what to do with these.
				// Struct.Fields             -- field
				// Interface.Methods         -- field
				// FuncType.{Params.Results} -- actionExpr
				// FuncDecl.Recv             -- actionExpr

			case *ast.File:
				// 'package foo'
				return path, actionPackage

			case *ast.ImportSpec:
				// TODO(adonovan): fix: why no package object? go/types bug?
				return path[1:], actionPackage

			default:
				// e.g. blank identifier
				// or y in "switch y := x.(type)"
				// or code in a _test.go file that's not part of the package.
				log.Printf("unknown reference %s in %T\n", n, path[1])
				return path, actionUnknown
			}

		case *ast.StarExpr:
			if pkginfo.Types[n].IsType() {
				return path, actionType
			}
			return path, actionExpr

		case ast.Expr:
			// All Expr but {BasicLit,Ident,StarExpr} are
			// "true" expressions that evaluate to a value.
			return path, actionExpr
		}

		// Ascend to parent.
		path = path[1:]
	}

	return nil, actionUnknown // unreachable
}
Example #6
0
func (scp *schemaParser) parseIdentProperty(pkg *loader.PackageInfo, expr *ast.Ident, prop swaggerTypable) error {
	// find the file this selector points to
	file, gd, ts, err := findSourceFile(pkg, expr.Name)
	if err != nil {
		err := swaggerSchemaForType(expr.Name, prop)
		if err != nil {
			return fmt.Errorf("package %s, error is: %v", pkg.String(), err)
		}
		return nil
	}
	if at, ok := ts.Type.(*ast.ArrayType); ok {
		// the swagger spec defines strfmt base64 as []byte.
		// in that case we don't actually want to turn it into an array
		// but we want to turn it into a string
		if _, ok := at.Elt.(*ast.Ident); ok {
			if strfmtName, ok := strfmtName(gd.Doc); ok {
				prop.Typed("string", strfmtName)
				return nil
			}
		}
		// this is a selector, so most likely not base64
		if strfmtName, ok := strfmtName(gd.Doc); ok {
			prop.Items().Typed("string", strfmtName)
			return nil
		}
	}

	// look at doc comments for swagger:strfmt [name]
	// when found this is the format name, create a schema with that name
	if strfmtName, ok := strfmtName(gd.Doc); ok {
		prop.Typed("string", strfmtName)
		return nil
	}
	switch tpe := ts.Type.(type) {
	case *ast.ArrayType:
		switch atpe := tpe.Elt.(type) {
		case *ast.Ident:
			return scp.parseIdentProperty(pkg, atpe, prop.Items())
		case *ast.SelectorExpr:
			return scp.typeForSelector(file, atpe, prop.Items())
		case *ast.StarExpr:
			return parseProperty(scp, file, atpe.X, prop.Items())
		default:
			return fmt.Errorf("unknown selector type: %#v", atpe)
		}
	case *ast.StructType:
		sd := newSchemaDecl(file, gd, ts)
		sd.inferNames()
		ref, err := spec.NewRef("#/definitions/" + sd.Name)
		if err != nil {
			return err
		}
		prop.SetRef(ref)
		scp.postDecls = append(scp.postDecls, *sd)
		return nil

	case *ast.Ident:
		return scp.parseIdentProperty(pkg, tpe, prop)

	case *ast.SelectorExpr:
		return scp.typeForSelector(file, tpe, prop)

	case *ast.InterfaceType:
		sd := newSchemaDecl(file, gd, ts)
		sd.inferNames()
		ref, err := spec.NewRef("#/definitions/" + sd.Name)
		if err != nil {
			return err
		}
		prop.SetRef(ref)
		scp.postDecls = append(scp.postDecls, *sd)
		return nil

	default:
		err := swaggerSchemaForType(expr.Name, prop)
		if err != nil {
			return fmt.Errorf("package %s, error is: %v", pkg.String(), err)
		}
		return nil
	}

}
Example #7
0
// IdentDoc attempts to get the documentation for a *ast.Ident.
func IdentDoc(ctxt *build.Context, id *ast.Ident, info *loader.PackageInfo, prog *loader.Program) (*Doc, error) {
	// get definition of identifier
	obj := info.ObjectOf(id)
	var pos string
	if p := obj.Pos(); p.IsValid() {
		pos = prog.Fset.Position(p).String()
	}

	pkgPath := ""
	if obj.Pkg() != nil {
		pkgPath = obj.Pkg().Path()
	}

	// handle packages imported under a different name
	if p, ok := obj.(*types.PkgName); ok {
		return PackageDoc(ctxt, prog.Fset, "", p.Imported().Path()) // SRCDIR TODO TODO
	}

	_, nodes, _ := prog.PathEnclosingInterval(obj.Pos(), obj.Pos())
	if len(nodes) == 0 {
		// special case - builtins
		doc, decl := findInBuiltin(obj.Name(), obj, prog)
		if doc != "" {
			return &Doc{
				Import: "builtin",
				Name:   obj.Name(),
				Doc:    doc,
				Decl:   decl,
				Pos:    pos,
			}, nil
		}
		return nil, fmt.Errorf("No documentation found for %s", obj.Name())
	}
	var doc *Doc
	for _, node := range nodes {
		switch node.(type) {
		case *ast.FuncDecl, *ast.GenDecl, *ast.Field:
		default:
			continue
		}
		doc = &Doc{
			Import: stripVendorFromImportPath(pkgPath),
			Name:   obj.Name(),
			Decl:   formatNode(node, obj, prog),
			Pos:    pos,
		}
		break
	}
	if doc == nil {
		// This shouldn't happen
		return nil, fmt.Errorf("No documentation found for %s", obj.Name())
	}

	for i, node := range nodes {
		//fmt.Printf("for %s: found %T\n%#v\n", id.Name, node, node)
		switch n := node.(type) {
		case *ast.FuncDecl:
			doc.Doc = n.Doc.Text()
			return doc, nil
		case *ast.GenDecl:
			constValue := ""
			if c, ok := obj.(*types.Const); ok {
				constValue = c.Val().ExactString()
			}

			if len(n.Specs) > 0 {
				switch n.Specs[0].(type) {
				case *ast.TypeSpec:
					spec := findTypeSpec(n, nodes[i-1].Pos())
					if spec.Doc != nil {
						doc.Doc = spec.Doc.Text()
					} else if spec.Comment != nil {
						doc.Doc = spec.Comment.Text()
					}
				case *ast.ValueSpec:
					spec := findVarSpec(n, nodes[i-1].Pos())
					if spec.Doc != nil {
						doc.Doc = spec.Doc.Text()
					} else if spec.Comment != nil {
						doc.Doc = spec.Comment.Text()
					}
				}
			}

			// if we didn't find doc for the spec, check the overall GenDecl
			if doc.Doc == "" && n.Doc != nil {
				doc.Doc = n.Doc.Text()
			}
			if constValue != "" {
				doc.Doc += fmt.Sprintf("\nConstant Value: %s", constValue)
			}

			return doc, nil
		case *ast.Field:
			// check the doc first, if not present, then look for a comment
			if n.Doc != nil {
				doc.Doc = n.Doc.Text()
				return doc, nil
			} else if n.Comment != nil {
				doc.Doc = n.Comment.Text()
				return doc, nil
			}
		}
	}
	return doc, nil
}
Example #8
0
// NewTransformer returns a transformer based on the specified template,
// a package containing "before" and "after" functions as described
// in the package documentation.
//
func NewTransformer(fset *token.FileSet, template *loader.PackageInfo, verbose bool) (*Transformer, error) {
	// Check the template.
	beforeSig := funcSig(template.Pkg, "before")
	if beforeSig == nil {
		return nil, fmt.Errorf("no 'before' func found in template")
	}
	afterSig := funcSig(template.Pkg, "after")
	if afterSig == nil {
		return nil, fmt.Errorf("no 'after' func found in template")
	}

	// TODO(adonovan): should we also check the names of the params match?
	if !types.Identical(afterSig, beforeSig) {
		return nil, fmt.Errorf("before %s and after %s functions have different signatures",
			beforeSig, afterSig)
	}

	templateFile := template.Files[0]
	for _, imp := range templateFile.Imports {
		if imp.Name != nil && imp.Name.Name == "." {
			// Dot imports are currently forbidden.  We
			// make the simplifying assumption that all
			// imports are regular, without local renames.
			//TODO document
			return nil, fmt.Errorf("dot-import (of %s) in template", imp.Path.Value)
		}
	}
	var beforeDecl, afterDecl *ast.FuncDecl
	for _, decl := range templateFile.Decls {
		if decl, ok := decl.(*ast.FuncDecl); ok {
			switch decl.Name.Name {
			case "before":
				beforeDecl = decl
			case "after":
				afterDecl = decl
			}
		}
	}

	before, err := soleExpr(beforeDecl)
	if err != nil {
		return nil, fmt.Errorf("before: %s", err)
	}
	after, err := soleExpr(afterDecl)
	if err != nil {
		return nil, fmt.Errorf("after: %s", err)
	}

	wildcards := make(map[*types.Var]bool)
	for i := 0; i < beforeSig.Params().Len(); i++ {
		wildcards[beforeSig.Params().At(i)] = true
	}

	// checkExprTypes returns an error if Tb (type of before()) is not
	// safe to replace with Ta (type of after()).
	//
	// Only superficial checks are performed, and they may result in both
	// false positives and negatives.
	//
	// Ideally, we would only require that the replacement be assignable
	// to the context of a specific pattern occurrence, but the type
	// checker doesn't record that information and it's complex to deduce.
	// A Go type cannot capture all the constraints of a given expression
	// context, which may include the size, constness, signedness,
	// namedness or constructor of its type, and even the specific value
	// of the replacement.  (Consider the rule that array literal keys
	// must be unique.)  So we cannot hope to prove the safety of a
	// transformation in general.
	Tb := template.TypeOf(before)
	Ta := template.TypeOf(after)
	if types.AssignableTo(Tb, Ta) {
		// safe: replacement is assignable to pattern.
	} else if tuple, ok := Tb.(*types.Tuple); ok && tuple.Len() == 0 {
		// safe: pattern has void type (must appear in an ExprStmt).
	} else {
		return nil, fmt.Errorf("%s is not a safe replacement for %s", Ta, Tb)
	}

	tr := &Transformer{
		fset:           fset,
		verbose:        verbose,
		wildcards:      wildcards,
		allowWildcards: true,
		seenInfos:      make(map[*types.Info]bool),
		importedObjs:   make(map[types.Object]*ast.SelectorExpr),
		before:         before,
		after:          after,
	}

	// Combine type info from the template and input packages, and
	// type info for the synthesized ASTs too.  This saves us
	// having to book-keep where each ast.Node originated as we
	// construct the resulting hybrid AST.
	//
	// TODO(adonovan): move type utility methods of PackageInfo to
	// types.Info, or at least into go/types.typeutil.
	tr.info.Info = types.Info{
		Types:      make(map[ast.Expr]types.TypeAndValue),
		Defs:       make(map[*ast.Ident]types.Object),
		Uses:       make(map[*ast.Ident]types.Object),
		Selections: make(map[*ast.SelectorExpr]*types.Selection),
	}
	mergeTypeInfo(&tr.info.Info, &template.Info)

	// Compute set of imported objects required by after().
	// TODO reject dot-imports in pattern
	ast.Inspect(after, func(n ast.Node) bool {
		if n, ok := n.(*ast.SelectorExpr); ok {
			if _, ok := tr.info.Selections[n]; !ok {
				// qualified ident
				obj := tr.info.Uses[n.Sel]
				tr.importedObjs[obj] = n
				return false // prune
			}
		}
		return true // recur
	})

	return tr, nil
}