Skip to content

Commit 1154155

Browse files
committed
Fix prototype generation for multiline functions
1 parent 2d3f2ee commit 1154155

File tree

4 files changed

+129
-9
lines changed

4 files changed

+129
-9
lines changed

src/arduino.cc/builder/ctags/ctags_parser.go

+108-6
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,15 @@ func (s *CTagsParser) Run(ctx *types.Context) error {
6666
tags = append(tags, parseTag(row))
6767
}
6868

69+
ctx.LinesInExternCContext = make(map[string][]int)
70+
71+
for _, tag := range tags {
72+
findLinesWithExternCScope(tag, ctx)
73+
}
74+
6975
skipTagsWhere(tags, tagIsUnknown, ctx)
7076
skipTagsWhere(tags, tagIsUnhandled, ctx)
71-
addPrototypes(tags)
77+
addPrototypes(tags, ctx)
7278
removeDefinedProtypes(tags, ctx)
7379
removeDuplicate(tags)
7480
skipTagsWhere(tags, prototypeAndCodeDontMatch, ctx)
@@ -78,15 +84,58 @@ func (s *CTagsParser) Run(ctx *types.Context) error {
7884
return nil
7985
}
8086

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) {
82131
for _, tag := range tags {
83132
if !tag.SkipMe {
84-
addPrototype(tag)
133+
addPrototype(tag, ctx)
85134
}
86135
}
87136
}
88137

89-
func addPrototype(tag *types.CTag) {
138+
func addPrototype(tag *types.CTag, ctx *types.Context) {
90139
if strings.Index(tag.Prototype, TEMPLATE) == 0 || strings.Index(tag.Code, TEMPLATE) == 0 {
91140
code := tag.Code
92141
if strings.Contains(code, "{") {
@@ -102,7 +151,8 @@ func addPrototype(tag *types.CTag) {
102151
if strings.Index(tag.Code, STATIC+" ") != -1 {
103152
tag.PrototypeModifiers = tag.PrototypeModifiers + " " + STATIC
104153
}
105-
if strings.Index(tag.Code, EXTERN+" ") != -1 {
154+
if strings.Index(tag.Code, EXTERN+" ") != -1 ||
155+
functionIsInNamespaceCnamespace(tag.Line, ctx.LinesInExternCContext[tag.Filename]) {
106156
tag.PrototypeModifiers = tag.PrototypeModifiers + " " + EXTERN
107157
}
108158
tag.PrototypeModifiers = strings.TrimSpace(tag.PrototypeModifiers)
@@ -190,13 +240,65 @@ func prototypeAndCodeDontMatch(tag *types.CTag) bool {
190240
prototype := removeSpacesAndTabs(tag.Prototype)
191241
prototype = removeTralingSemicolon(prototype)
192242

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
194291
}
195292

196293
func removeTralingSemicolon(s string) string {
197294
return s[0 : len(s)-1]
198295
}
199296

297+
func removeEverythingAfterClosingPerentheses(s string) string {
298+
n := strings.Index(s, ")")
299+
return s[0 : n+1]
300+
}
301+
200302
func removeSpacesAndTabs(s string) string {
201303
s = strings.Replace(s, " ", "", -1)
202304
s = strings.Replace(s, "\t", "", -1)

src/arduino.cc/builder/test/sketch_with_externC_multiline/sketch_with_externC_multiline.ino

+5-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ void setup() {
55

66
void loop() {
77
// put your main code here, to run repeatedly:
8-
8+
test2();
99
}
1010

11+
void
12+
test() {}
13+
1114
extern "C" {
12-
void test() {}
15+
void test2() {}
1316
}

src/arduino.cc/builder/test/sketch_with_multiline_prototypes/sketch_with_multiline_prototypes.ino

+13-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,17 @@ int b,
55
int c,
66
int d) { }
77

8-
void loop() {}
8+
void loop() {
9+
mymultilinetestfunc();
10+
mymultilinetestfunc2();
11+
}
912

13+
int
14+
mymultilinetestfunc() {
15+
16+
}
17+
18+
int
19+
mymultilinetestfunc2() {
20+
21+
}

src/arduino.cc/builder/types/context.go

+3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ type Context struct {
8686

8787
// ReadFileAndStoreInContext command
8888
FileToRead string
89+
90+
// Sketch lines with extern "C" context
91+
LinesInExternCContext map[string][]int
8992
}
9093

9194
func (ctx *Context) ExtractBuildOptions() properties.Map {

0 commit comments

Comments
 (0)