88 "io"
99 "os"
1010 "strings"
11+ "unicode"
1112
1213 a "testsuites/annotations"
1314 c "testsuites/collector"
@@ -26,6 +27,7 @@ const (
2627 BLOCK NodeType = "block"
2728 CALL_EXPRESSION NodeType = "call_expression"
2829 IDENTIFIER NodeType = "identifier"
30+ METHOD_DECLARATION NodeType = "method_declaration"
2931)
3032
3133type Metadata struct {
@@ -39,6 +41,11 @@ type FileData struct {
3941 Functions []c.Function
4042}
4143
44+ type FunctionAnnotationNode struct {
45+ Node * sitter.Node
46+ Function c.FunctionAnnotation
47+ }
48+
4249type hasNoTests bool
4350
4451func ExtractInfo (file c.TestFile , ctx context.Context , fileID c.FileID ) (* FileData , hasNoTests , error ) {
@@ -81,6 +88,29 @@ func ExtractInfo(file c.TestFile, ctx context.Context, fileID c.FileID) (*FileDa
8188 return fileData , hasNoTests (! checkForExistanceOfTests (fData )), nil
8289}
8390
91+ func GetExportedFunctions (ctx context.Context , filePath string ) ([]c.FunctionAnnotation , error ) {
92+ content , err := getFileContent (filePath )
93+ if err != nil {
94+ return nil , err
95+ }
96+
97+ parser := sitter .NewParser ()
98+ parser .SetLanguage (golang .GetLanguage ())
99+
100+ tree , err := parser .ParseCtx (ctx , nil , []byte (content ))
101+ if err != nil {
102+ return nil , err
103+ }
104+
105+ cursor := sitter .NewTreeCursor (tree .RootNode ())
106+ fnsAnno , err := parseContentForFunctions (content , cursor )
107+ if err != nil {
108+ return nil , err
109+ }
110+
111+ return fnsAnno , nil
112+ }
113+
84114func getFileContent (filePath string ) (content string , err error ) {
85115
86116 file , err := os .Open (filePath )
@@ -103,17 +133,17 @@ func parseContent(content string, treeCursor *sitter.TreeCursor, filePath string
103133 var annotationParser a.Parser
104134
105135 fileData .Metadata = getMetadata (content , treeCursor , & annotationParser )
106- functions := getFunctionNodes (content , treeCursor , & annotationParser )
136+ funcNodes := getFunctionNodes (content , treeCursor , & annotationParser )
107137
108- for _ , function := range functions {
138+ for _ , funcNode := range funcNodes {
109139
110- behaviors := findBehaviorsFromNode (content , function .Node )
140+ behaviors := findBehaviorsFromNode (content , funcNode .Node )
111141 var callExpressions []string = nil
112142 if len (behaviors ) > 0 {
113- callExpressions = findCallExprFromNode (content , function .Node )
143+ callExpressions = findCallExprFromNode (content , funcNode .Node )
114144 }
115145
116- fileData .Functions = append (fileData .Functions , makeCollectorScenario (filePath , function .Name , behaviors , callExpressions ))
146+ fileData .Functions = append (fileData .Functions , makeCollectorScenario (filePath , funcNode . Function .Name , behaviors , callExpressions ))
117147
118148 }
119149
@@ -163,10 +193,11 @@ func getMetadata(content string, treeCursor *sitter.TreeCursor, parser *a.Parser
163193 return & meta
164194}
165195
166- func getFunctionNodes (content string , treeCursor * sitter.TreeCursor , parser * a.Parser ) (funcAnnoPair []struct {
167- Node * sitter.Node
168- Name string
169- }) {
196+ // getFunctionNodes method extracts functions and methods.
197+ // Function can have 2 types: function_declaration (for example contructor)
198+ // and method_declaration (can be exported and unexported).
199+ // Return value is a slice of FunctionAnnotationNode, where each node holds function's annotation.
200+ func getFunctionNodes (content string , treeCursor * sitter.TreeCursor , parser * a.Parser ) (funcAnnoPair []FunctionAnnotationNode ) {
170201
171202 numChildsRootNode := treeCursor .CurrentNode ().ChildCount ()
172203 node := & sitter.Node {}
@@ -203,18 +234,48 @@ func getFunctionNodes(content string, treeCursor *sitter.TreeCursor, parser *a.P
203234 continue
204235 }
205236
206- funcAnnoPair = append (funcAnnoPair , struct {
207- Node * sitter.Node
208- Name string
209- }{
237+ funcAnnoPair = append (funcAnnoPair , FunctionAnnotationNode {
210238 Node : node ,
211- Name : funcName ,
239+ Function : c.FunctionAnnotation {
240+ Name : funcName ,
241+ },
212242 })
213243
214244 node = nil
215245 funcName = ""
216246 isIgnored = false
217247 }
248+ /*
249+ Take a look at this function for example:
250+ *********
251+ func (e *ProofEventStream) ListenProofEvent(
252+ ctx context.Context,
253+ policy *types2.ProofRegisterPolicy)
254+ (<-chan *types2.RequestEvent, error){...}
255+ *********
256+ child.Child(1) is a first part of function declaration (e *ProofEventStream)
257+ child.Child(2): field_identifier 'ListenProofEvent'
258+ child.Child(3): parameter_list: (ctx context.Context, policy *types2.ProofRegisterPolicy)
259+ child.Child(4): parameter_list: (<-chan *types2.RequestEvent, error)
260+ */
261+ if child .Type () == string (METHOD_DECLARATION ) {
262+ funcName = content [child .Child (2 ).StartByte ():child .Child (2 ).EndByte ()]
263+ params := ""
264+ returnValues := ""
265+ if child .Child (1 ).Type () == string (PARAMETER_LIST ) {
266+ params = content [child .Child (3 ).StartByte ():child .Child (3 ).EndByte ()]
267+ returnValues = content [child .Child (4 ).StartByte ():child .Child (4 ).EndByte ()]
268+ }
269+ funcAnnoPair = append (funcAnnoPair , FunctionAnnotationNode {
270+ Node : child ,
271+ Function : c.FunctionAnnotation {
272+ Name : funcName ,
273+ Public : isPublic (funcName ),
274+ InputParams : params ,
275+ ReturnValues : returnValues ,
276+ },
277+ })
278+ }
218279 prevNode = child
219280 }
220281 }
@@ -301,3 +362,21 @@ func makeID(filePath string, funcName string, behavior string) string {
301362 hash := md5 .Sum ([]byte (fmt .Sprintf ("%s_%s_%s" , filePath , funcName , behavior )))
302363 return string (hex .EncodeToString (hash [:]))
303364}
365+
366+ // parseContentForFunctions accepts a content which represents whole golang file as a string,
367+ // parses it and returns a slice of function annotations (including exported and unexported ones).
368+ func parseContentForFunctions (content string , cursor * sitter.TreeCursor ) ([]c.FunctionAnnotation , error ) {
369+ var annotationParser a.Parser
370+
371+ var fnsAnno []c.FunctionAnnotation
372+ funcNodes := getFunctionNodes (content , cursor , & annotationParser )
373+ for _ , funcNode := range funcNodes {
374+ fnsAnno = append (fnsAnno , funcNode .Function )
375+ }
376+
377+ return fnsAnno , nil
378+ }
379+
380+ func isPublic (funcName string ) bool {
381+ return unicode .IsUpper (rune (funcName [0 ]))
382+ }
0 commit comments