github.com/code-visible/golang@v0.0.0-20240214000051-0f9c587b0b32/sourcefile/file.go (about)

     1  package sourcefile
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/token"
     7  	"path/filepath"
     8  
     9  	"github.com/code-visible/golang/callhierarchy"
    10  	"github.com/code-visible/golang/node"
    11  )
    12  
    13  type SourceFile struct {
    14  	ID        string               `json:"id"`
    15  	Name      string               `json:"name"`
    16  	Path      string               `json:"path"`
    17  	Pkg       string               `json:"pkg"`
    18  	Callables []*node.Callable     `json:"callables"`
    19  	Abstracts []*node.Abstract     `json:"abstracts"`
    20  	Calls     []callhierarchy.Call `json:"calls"`
    21  	Deps      []string             `json:"deps"`
    22  
    23  	abstracts map[string]*node.Abstract
    24  	callables map[string]*node.Callable
    25  	parsed    *ast.File
    26  	pkg       *ast.Package
    27  	fset      *token.FileSet
    28  }
    29  
    30  func NewSourceFile(pkg string, path string, file *ast.File, fset *token.FileSet) *SourceFile {
    31  	return &SourceFile{
    32  		ID:        fmt.Sprintf("%s/%s", pkg, filepath.Base(path)),
    33  		Name:      filepath.Base(path),
    34  		Path:      filepath.Dir(path),
    35  		Pkg:       pkg,
    36  		Callables: nil,
    37  		Abstracts: nil,
    38  		Deps:      nil,
    39  		abstracts: make(map[string]*node.Abstract),
    40  		callables: make(map[string]*node.Callable),
    41  		parsed:    file,
    42  		pkg:       nil,
    43  		fset:      fset,
    44  	}
    45  }
    46  
    47  func (sf *SourceFile) EnumerateCallables() {
    48  	for _, decl := range sf.parsed.Decls {
    49  		fn, ok := decl.(*ast.FuncDecl)
    50  		if !ok {
    51  			continue
    52  		}
    53  		c := &node.Callable{
    54  			ID:          "",
    55  			Pos:         sf.fset.Position(fn.Pos()).String(),
    56  			Name:        fn.Name.Name,
    57  			Abstract:    "",
    58  			Comment:     fn.Doc.Text(),
    59  			File:        sf.Name,
    60  			Pkg:         sf.Pkg,
    61  			Typ:         "",
    62  			Syscalls:    make([]string, 0),
    63  			Parameters:  make([]string, 0),
    64  			Results:     make([]string, 0),
    65  			Description: "",
    66  			Method:      false,
    67  			Private:     false,
    68  			Orphan:      false,
    69  		}
    70  		sf.callables[fn.Name.Name] = c
    71  		sf.Callables = append(sf.Callables, c)
    72  	}
    73  }
    74  
    75  func (sf *SourceFile) EnumerateAbstracts() {
    76  	for _, decl := range sf.parsed.Decls {
    77  		if decl, ok := decl.(*ast.GenDecl); ok {
    78  			for _, spec := range decl.Specs {
    79  				if spec, ok := spec.(*ast.TypeSpec); ok {
    80  					// TODO:
    81  					ab := &node.Abstract{
    82  						ID:   "",
    83  						Name: spec.Name.String(),
    84  					}
    85  					sf.abstracts[spec.Name.String()] = ab
    86  					sf.Abstracts = append(sf.Abstracts, ab)
    87  				}
    88  			}
    89  		}
    90  	}
    91  }
    92  
    93  func (sf *SourceFile) EnumerateCallHierarchy() {
    94  	var trace []ast.Node
    95  	ast.Inspect(sf.parsed, func(n ast.Node) bool {
    96  		if n == nil {
    97  			trace = trace[:len(trace)-1]
    98  		} else {
    99  			trace = append(trace, n)
   100  		}
   101  
   102  		if x, ok := n.(*ast.CallExpr); ok {
   103  			call := callhierarchy.Call{
   104  				ID:        "",
   105  				Caller:    "universe",
   106  				Callee:    "",
   107  				File:      sf.Name,
   108  				Typ:       "",
   109  				CallerPos: -1,
   110  				CalleePos: n.Pos(),
   111  			}
   112  			for i := len(trace) - 2; i >= 0; i-- {
   113  				if fnDecl, ok := trace[i].(*ast.FuncDecl); ok {
   114  					call.Caller = fnDecl.Name.Name
   115  					call.CallerPos = trace[i].Pos()
   116  					break
   117  				}
   118  			}
   119  
   120  			switch fnID := x.Fun.(type) {
   121  			case *ast.Ident:
   122  				call.Callee = fnID.Name
   123  			case *ast.SelectorExpr:
   124  				if scope, ok := fnID.X.(*ast.Ident); ok {
   125  					call.Callee = fmt.Sprintf("%s.%s", scope.Name, fnID.Sel.Name)
   126  				} else {
   127  					call.Callee = fmt.Sprintf("unknown.%s", fnID.Sel.Name)
   128  				}
   129  			default:
   130  				panic("parse call error, not covered case")
   131  			}
   132  
   133  			sf.Calls = append(sf.Calls, call)
   134  		}
   135  
   136  		return true
   137  	})
   138  }