@@ -66,9 +66,15 @@ func (s *CTagsParser) Run(ctx *types.Context) error {
66
66
tags = append (tags , parseTag (row ))
67
67
}
68
68
69
+ ctx .LinesInExternCContext = make (map [string ][]int )
70
+
71
+ for _ , tag := range tags {
72
+ findLinesWithExternCScope (tag , ctx )
73
+ }
74
+
69
75
skipTagsWhere (tags , tagIsUnknown , ctx )
70
76
skipTagsWhere (tags , tagIsUnhandled , ctx )
71
- addPrototypes (tags )
77
+ addPrototypes (tags , ctx )
72
78
removeDefinedProtypes (tags , ctx )
73
79
removeDuplicate (tags )
74
80
skipTagsWhere (tags , prototypeAndCodeDontMatch , ctx )
@@ -78,15 +84,58 @@ func (s *CTagsParser) Run(ctx *types.Context) error {
78
84
return nil
79
85
}
80
86
81
- func addPrototypes (tags []* types.CTag ) {
87
+ func findLinesWithExternCScope (tag * types.CTag , ctx * types.Context ) {
88
+
89
+ if ctx .LinesInExternCContext [tag .Filename ] != nil {
90
+ return
91
+ }
92
+
93
+ file , err := os .Open (tag .Filename )
94
+ if err == nil {
95
+ defer file .Close ()
96
+
97
+ ctx .LinesInExternCContext [tag .Filename ] = append (ctx .LinesInExternCContext [tag .Filename ], 0 )
98
+
99
+ scanner := bufio .NewScanner (file )
100
+
101
+ inExternCNamespaceScope := false
102
+ indentLevels := 0
103
+ line := 0
104
+ for scanner .Scan () {
105
+ line ++
106
+ str := scanner .Text ()
107
+ if strings .Contains (str , EXTERN ) {
108
+ inExternCNamespaceScope = true
109
+ }
110
+ if inExternCNamespaceScope == true {
111
+ ctx .LinesInExternCContext [tag .Filename ] = append (ctx .LinesInExternCContext [tag .Filename ], line )
112
+ }
113
+ indentLevels += strings .Count (str , "{" ) - strings .Count (str , "}" )
114
+ if indentLevels == 0 {
115
+ inExternCNamespaceScope = false
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ func functionIsInNamespaceCnamespace (line int , index []int ) bool {
122
+ for _ , lines := range index {
123
+ if line == lines {
124
+ return true
125
+ }
126
+ }
127
+ return false
128
+ }
129
+
130
+ func addPrototypes (tags []* types.CTag , ctx * types.Context ) {
82
131
for _ , tag := range tags {
83
132
if ! tag .SkipMe {
84
- addPrototype (tag )
133
+ addPrototype (tag , ctx )
85
134
}
86
135
}
87
136
}
88
137
89
- func addPrototype (tag * types.CTag ) {
138
+ func addPrototype (tag * types.CTag , ctx * types. Context ) {
90
139
if strings .Index (tag .Prototype , TEMPLATE ) == 0 || strings .Index (tag .Code , TEMPLATE ) == 0 {
91
140
code := tag .Code
92
141
if strings .Contains (code , "{" ) {
@@ -102,7 +151,8 @@ func addPrototype(tag *types.CTag) {
102
151
if strings .Index (tag .Code , STATIC + " " ) != - 1 {
103
152
tag .PrototypeModifiers = tag .PrototypeModifiers + " " + STATIC
104
153
}
105
- if strings .Index (tag .Code , EXTERN + " " ) != - 1 {
154
+ if strings .Index (tag .Code , EXTERN + " " ) != - 1 ||
155
+ functionIsInNamespaceCnamespace (tag .Line , ctx .LinesInExternCContext [tag .Filename ]) {
106
156
tag .PrototypeModifiers = tag .PrototypeModifiers + " " + EXTERN
107
157
}
108
158
tag .PrototypeModifiers = strings .TrimSpace (tag .PrototypeModifiers )
@@ -190,13 +240,65 @@ func prototypeAndCodeDontMatch(tag *types.CTag) bool {
190
240
prototype := removeSpacesAndTabs (tag .Prototype )
191
241
prototype = removeTralingSemicolon (prototype )
192
242
193
- return strings .Index (code , prototype ) == - 1
243
+ // is the code contained in the prototype?
244
+ ret := strings .Index (code , prototype )
245
+
246
+ if ret == - 1 {
247
+ // what could possibly go wrong?
248
+ // definition is multiline
249
+
250
+ // Phase 1: add to code n non-whitespace tokens before the code line
251
+ code = removeEverythingAfterClosingPerentheses (code )
252
+
253
+ // is the code contained in the prototype?
254
+ n := strings .Index (prototype , code )
255
+
256
+ code = getFunctionPrototypWithNPreviousCharacter (tag , code , n )
257
+ ret = strings .Index (code , prototype )
258
+ }
259
+
260
+ return ret == - 1
261
+ }
262
+
263
+ func getFunctionPrototypWithNPreviousCharacter (tag * types.CTag , code string , n int ) string {
264
+
265
+ expectedPrototypeLen := len (code ) + n
266
+
267
+ file , err := os .Open (tag .Filename )
268
+ if err == nil {
269
+ defer file .Close ()
270
+
271
+ scanner := bufio .NewScanner (file )
272
+ line := 1
273
+ iteration := 1
274
+
275
+ for len (code ) < expectedPrototypeLen {
276
+
277
+ // skip lines until we get to the start of this tag
278
+ for scanner .Scan () && line < (tag .Line - iteration ) {
279
+ line ++
280
+ }
281
+
282
+ code = scanner .Text () + code
283
+ code = removeSpacesAndTabs (code )
284
+ iteration ++
285
+ // rewind the file
286
+ file .Seek (0 , 0 )
287
+ line = 0
288
+ }
289
+ }
290
+ return code
194
291
}
195
292
196
293
func removeTralingSemicolon (s string ) string {
197
294
return s [0 : len (s )- 1 ]
198
295
}
199
296
297
+ func removeEverythingAfterClosingPerentheses (s string ) string {
298
+ n := strings .Index (s , ")" )
299
+ return s [0 : n + 1 ]
300
+ }
301
+
200
302
func removeSpacesAndTabs (s string ) string {
201
303
s = strings .Replace (s , " " , "" , - 1 )
202
304
s = strings .Replace (s , "\t " , "" , - 1 )
0 commit comments