8
8
"io"
9
9
"os"
10
10
"strings"
11
+ "unicode"
11
12
12
13
a "testsuites/annotations"
13
14
c "testsuites/collector"
@@ -26,6 +27,7 @@ const (
26
27
BLOCK NodeType = "block"
27
28
CALL_EXPRESSION NodeType = "call_expression"
28
29
IDENTIFIER NodeType = "identifier"
30
+ METHOD_DECLARATION NodeType = "method_declaration"
29
31
)
30
32
31
33
type Metadata struct {
@@ -39,6 +41,11 @@ type FileData struct {
39
41
Functions []c.Function
40
42
}
41
43
44
+ type FunctionAnnotationNode struct {
45
+ Node * sitter.Node
46
+ Function c.FunctionAnnotation
47
+ }
48
+
42
49
type hasNoTests bool
43
50
44
51
func 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
81
88
return fileData , hasNoTests (! checkForExistanceOfTests (fData )), nil
82
89
}
83
90
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
+
84
114
func getFileContent (filePath string ) (content string , err error ) {
85
115
86
116
file , err := os .Open (filePath )
@@ -103,17 +133,17 @@ func parseContent(content string, treeCursor *sitter.TreeCursor, filePath string
103
133
var annotationParser a.Parser
104
134
105
135
fileData .Metadata = getMetadata (content , treeCursor , & annotationParser )
106
- functions := getFunctionNodes (content , treeCursor , & annotationParser )
136
+ funcNodes := getFunctionNodes (content , treeCursor , & annotationParser )
107
137
108
- for _ , function := range functions {
138
+ for _ , funcNode := range funcNodes {
109
139
110
- behaviors := findBehaviorsFromNode (content , function .Node )
140
+ behaviors := findBehaviorsFromNode (content , funcNode .Node )
111
141
var callExpressions []string = nil
112
142
if len (behaviors ) > 0 {
113
- callExpressions = findCallExprFromNode (content , function .Node )
143
+ callExpressions = findCallExprFromNode (content , funcNode .Node )
114
144
}
115
145
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 ))
117
147
118
148
}
119
149
@@ -163,10 +193,11 @@ func getMetadata(content string, treeCursor *sitter.TreeCursor, parser *a.Parser
163
193
return & meta
164
194
}
165
195
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 ) {
170
201
171
202
numChildsRootNode := treeCursor .CurrentNode ().ChildCount ()
172
203
node := & sitter.Node {}
@@ -203,18 +234,48 @@ func getFunctionNodes(content string, treeCursor *sitter.TreeCursor, parser *a.P
203
234
continue
204
235
}
205
236
206
- funcAnnoPair = append (funcAnnoPair , struct {
207
- Node * sitter.Node
208
- Name string
209
- }{
237
+ funcAnnoPair = append (funcAnnoPair , FunctionAnnotationNode {
210
238
Node : node ,
211
- Name : funcName ,
239
+ Function : c.FunctionAnnotation {
240
+ Name : funcName ,
241
+ },
212
242
})
213
243
214
244
node = nil
215
245
funcName = ""
216
246
isIgnored = false
217
247
}
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
+ }
218
279
prevNode = child
219
280
}
220
281
}
@@ -301,3 +362,21 @@ func makeID(filePath string, funcName string, behavior string) string {
301
362
hash := md5 .Sum ([]byte (fmt .Sprintf ("%s_%s_%s" , filePath , funcName , behavior )))
302
363
return string (hex .EncodeToString (hash [:]))
303
364
}
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