func getSourceFiles(pkgPath string, deep bool) ([]string, error) { pkgDir, err := os.Open(pkgPath) if err != nil { util.Err("error while opening package at path [%s]\n%v", pkgPath, err) return nil, err } filesInfos, err := pkgDir.Readdir(0) if err != nil { util.Err("error reading opening package at path [%s]\n%v", pkgPath, err) return nil, err } var files []string for _, f := range filesInfos { if f.IsDir() && deep && isValidSourceDir(f.Name()) { util.Debug("append folder [%s]", f.Name()) dirFiles, err := getSourceFiles(pkgPath+"/"+f.Name(), true) if err != nil { return dirFiles, err } files = append(files, dirFiles...) } else if isValidSourceFile(f.Name()) { util.Debug("append file [%s]", f.Name()) if strings.HasSuffix(f.Name(), "_test.go") { files = append(files, pkgPath+"/"+f.Name()) } else { files = append([]string{pkgPath + "/" + f.Name()}, files...) } } } util.Info("FILES [%v]", files) return files, nil }
func addInterface(obj types.Object, ident *ast.Ident, ctx *getDefinitionsContext) { interfac := obj.Type().Underlying().(*types.Interface) def := createDef(obj, ident, ctx, true) updateGetDefinitionsContext(ctx, def, ident, obj) util.Debug("adding interface [%s] [%v] [%v] [%v]", def.Name, def.Pkg, obj.Type().Underlying(), obj.Type()) //Adding all methods of interface for i := 0; i < interfac.NumMethods(); i++ { f := interfac.Method(i) def := createDef(f, nil, ctx, false) util.Debug("\tadding method [%v] [%s]", f, def.Name) updateGetDefinitionsContext(ctx, def, ident, f) } }
//Collect going through package and collect info //using conf.Check method. It's using this implementation //of importer for check all inner packages and go/types/importer.Default() //to check all built in packages (without sources) func (_importer *CollectInfoImporter) Collect() (*types.Package, *token.FileSet, error) { var conf types.Config conf.Importer = _importer conf.Error = _importer.errorHandler if _importer.packages == nil { _importer.packages = make(map[string]*types.Package) } var pkg *types.Package var err error var files []string if files, err = fs.SourceFiles(_importer.Pkg, false); err != nil { return nil, nil, err } if _importer.fset, _importer.astFiles, err = doParseFiles(files, _importer.fset); err != nil { return nil, nil, err } //XXX: return positive result if check() returns error. pkg, _ = conf.Check(_importer.Pkg, _importer.fset, _importer.astFiles, _importer.Info) // if pkg, err = conf.Check(_importer.Pkg, _importer.fset, _importer.astFiles, _importer.Info); err != nil { // return pkg, _importer.fset, err // } _importer.packages[_importer.Pkg] = pkg util.Debug("package [%s] successfully parsed\n", pkg.Name()) return pkg, _importer.fset, nil }
func doParseFiles(filePathes []string, fset *token.FileSet) (*token.FileSet, []*ast.File, error) { if fset == nil { fset = token.NewFileSet() } util.Info("parsing files %v", filePathes) astFiles := make([]*ast.File, 0, len(filePathes)) for _, f := range filePathes { //XXX: Ignoring files with packages ends with _test. //XXX: Doing that because getting error in check() //XXX: cause source file is still going to current //XXX: packages. Need to analyze package before //XXX: and check both packages separately. tempFset := token.NewFileSet() astFile, err := parser.ParseFile(tempFset, f, nil, 0) if !strings.HasSuffix(astFile.Name.Name, "_test") { if err != nil { return nil, nil, err } astFile, _ := parser.ParseFile(fset, f, nil, 0) astFiles = append(astFiles, astFile) } } iterateFunc := func(f *token.File) bool { util.Debug("\t%s", f.Name()) return true } fset.Iterate(iterateFunc) return fset, astFiles, nil }
func logType(t types.TypeAndValue) { if t.Type != nil { util.Debug("type [%s] [%s] [%s] [%s]", reflect.TypeOf(t.Type), t.Type.String(), reflect.TypeOf(t.Type.Underlying()), t.Type.Underlying().String()) switch t.Type.(type) { case *types.Signature: s := t.Type.(*types.Signature) if s.Recv() != nil { util.Info("\t\t[%s] [%s]", s.Recv(), s.Recv().Type().String()) } if tuple := s.Params(); tuple != nil { for i := 0; i < tuple.Len(); i++ { v := tuple.At(i) util.Debug("\t\t%s", v.Name()) if types.IsInterface(v.Type()) { util.Debug("\t\t\t<------interface") } } } } } }
func lookupMethod(def *Definition, iDef *Definition, ctx *getDefinitionsContext) *Definition { methodName := iDef.Name + "." + def.SimpleName externalInterfaceMethodName := "interface." + def.SimpleName def.InterfacesDefs = append(def.InterfacesDefs, iDef) if methodDef := ctx.defs[methodName]; methodDef != nil { return methodDef } else if methodDef := ctx.defs[externalInterfaceMethodName]; methodDef != nil { return methodDef } else { util.Debug("can't find method [%s]", methodName) } return nil }
func isUsed(def *Definition) bool { used := true if len(def.Usages) == 0 { used = false } else { //Checking pathes of usages to not count internal hasExternalUsages := false util.Debug("checking [%s]", def.Name) for _, u := range def.Usages { pkgPath := "" if def.Pkg != nil { pkgPath = def.Pkg.Path() } else if dotIdx := strings.LastIndex(def.Name, "."); dotIdx >= 0 { pkgPath = def.Name[0:dotIdx] } util.Debug("checking [%v]", u.Pos) if u.Pos.IsValid() && fs.GetPackagePath(u.Pos.Filename) != pkgPath { hasExternalUsages = true break } } used = hasExternalUsages } if !used { //Check all interfaces for _, i := range def.Interfaces { if isUsed(i) { used = true break } } } return used }
func implements(t types.Type, interfac *types.Interface, pkg *types.Package) bool { if interfac == nil || t == nil || interfac.Empty() { return false } if types.Implements(t, interfac) { return true } //For some reason, interfaces that comes //already built in (not from sources) are //not working with types.Implements method for i := 0; i < interfac.NumMethods(); i++ { m := interfac.Method(i) obj, _, _ := types.LookupFieldOrMethod(t, true, pkg, m.Name()) if obj == nil { util.Debug("method %s not found in type %v", m.Name(), t) return false } } return true }
func fillInterfaces(def *Definition, obj types.Object, ctx *getDefinitionsContext) { switch obj.(type) { case *types.TypeName: //Filling information about implemented //interfaces to type's definition. typ := obj.(*types.TypeName) if typ.Type() != nil { for _, di := range ctx.interfaces { if di.interfac != nil && di.def != nil && implements(typ.Type(), di.interfac, typ.Pkg()) { def.InterfacesDefs = append(def.InterfacesDefs, di.def) } } } case *types.Func: f := obj.(*types.Func) underlyingType := f.Type().Underlying() switch underlyingType.(type) { case *types.Signature: s := underlyingType.(*types.Signature) if s.Recv() != nil { //Getting all interfaces from function's receiver and //searching for current function in each interface. //If found, then adding method's definition to function's //interfaces recvTypeName := strings.Replace(s.Recv().Type().String(), "*", "", 1) if typeDef, ok := ctx.defs[recvTypeName]; ok { for _, iDef := range typeDef.InterfacesDefs { def.InterfacesDefs = append(def.InterfacesDefs, iDef) if methodDef := lookupMethod(def, iDef, ctx); methodDef != nil { def.InterfacesDefs = append(def.InterfacesDefs, methodDef) } } } else { util.Debug("recv type not found [%s]", s.Recv().Type().String()) } } } } }