From 251ddaea30fee5809e6862820c471d0dd45ec92a Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 29 Nov 2016 15:02:11 +0100 Subject: [PATCH 01/11] Add tests for C linkage proto generation --- .../sketch_with_externC_multiline.ino | 34 +++++++++++++++++++ .../try_build_of_problematic_sketch_test.go | 4 +++ 2 files changed, 38 insertions(+) create mode 100644 src/arduino.cc/builder/test/sketch_with_externC_multiline/sketch_with_externC_multiline.ino diff --git a/src/arduino.cc/builder/test/sketch_with_externC_multiline/sketch_with_externC_multiline.ino b/src/arduino.cc/builder/test/sketch_with_externC_multiline/sketch_with_externC_multiline.ino new file mode 100644 index 00000000..e911a4b0 --- /dev/null +++ b/src/arduino.cc/builder/test/sketch_with_externC_multiline/sketch_with_externC_multiline.ino @@ -0,0 +1,34 @@ +void setup() { + // put your setup code here, to run once: + } + + void loop() { + // put your main code here, to run repeatedly: + test2(); + test4(); + test6(); + test7(); + test10(); + } + + extern "C" { + void test2() {} + } + + extern "C" + { + void test4() {} + } + + extern "C" + + { + void test6() {} + } + + // this function should not have C linkage + void test7() {} + + extern "C" void test10() { + + }; \ No newline at end of file diff --git a/src/arduino.cc/builder/test/try_build_of_problematic_sketch_test.go b/src/arduino.cc/builder/test/try_build_of_problematic_sketch_test.go index 5cbbf5cf..0e0e7921 100644 --- a/src/arduino.cc/builder/test/try_build_of_problematic_sketch_test.go +++ b/src/arduino.cc/builder/test/try_build_of_problematic_sketch_test.go @@ -205,6 +205,10 @@ func TestTryBuild039(t *testing.T) { tryBuildWithContext(t, ctx, "sketch12", "sketch12.ino") } +func TestTryBuild040(t *testing.T) { + tryBuild(t, "sketch_with_externC_multiline", "sketch_with_externC_multiline.ino") +} + func makeDefaultContext(t *testing.T) *types.Context { DownloadCoresAndToolsAndLibraries(t) From 991949b26695c075ea34e00f8c61d518428f7cb1 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 29 Nov 2016 15:02:27 +0100 Subject: [PATCH 02/11] Add step to correct tags data C linkage --- .../builder/ctags/ctags_has_issues.go | 139 ++++++++++++++++++ src/arduino.cc/builder/ctags_runner.go | 2 + 2 files changed, 141 insertions(+) create mode 100644 src/arduino.cc/builder/ctags/ctags_has_issues.go diff --git a/src/arduino.cc/builder/ctags/ctags_has_issues.go b/src/arduino.cc/builder/ctags/ctags_has_issues.go new file mode 100644 index 00000000..4d4750af --- /dev/null +++ b/src/arduino.cc/builder/ctags/ctags_has_issues.go @@ -0,0 +1,139 @@ +/* + * This file is part of Arduino Builder. + * + * Arduino Builder is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As a special exception, you may use this file as part of a free software + * library without restriction. Specifically, if other files instantiate + * templates or use macros or inline functions from this file, or you compile + * this file and link it with other files to produce an executable, this + * file does not by itself cause the resulting executable to be covered by + * the GNU General Public License. This exception does not however + * invalidate any other reasons why the executable file might be covered by + * the GNU General Public License. + * + * Copyright 2015 Arduino LLC (http://www.arduino.cc/) + */ + +package ctags + +import ( + "bufio" + "os" + "strings" + + "arduino.cc/builder/types" +) + +func (p *CTagsParser) FixCLinkageTagsDeclarations(tags []*types.CTag) { + + linesMap := p.FindCLinkageLines(tags) + for i, _ := range tags { + + if sliceContainsInt(linesMap[tags[i].Filename], tags[i].Line) && + !strings.Contains(tags[i].PrototypeModifiers, EXTERN) { + tags[i].PrototypeModifiers = tags[i].PrototypeModifiers + " " + EXTERN + } + } +} + +func sliceContainsInt(s []int, e int) bool { + for _, a := range s { + if a == e { + return true + } + } + return false +} + +/* This function scans the source files searching for "extern C" context + * It save the line numbers in a map filename -> {lines...} + */ +func (p *CTagsParser) FindCLinkageLines(tags []*types.CTag) map[string][]int { + + lines := make(map[string][]int) + + for _, tag := range tags { + + if lines[tag.Filename] != nil { + break + } + + file, err := os.Open(tag.Filename) + if err == nil { + defer file.Close() + + lines[tag.Filename] = append(lines[tag.Filename], 0) + + scanner := bufio.NewScanner(file) + + // we can't remove the comments otherwise the line number will be wrong + // there are three cases: + // 1 - extern "C" void foo() + // 2 - extern "C" { + // void foo(); + // void bar(); + // } + // 3 - extern "C" + // { + // void foo(); + // void bar(); + // } + // case 1 and 2 can be simply recognized with string matching and indent level count + // case 3 needs specia attention: if the line ONLY contains `extern "C"` string, don't bail out on indent level = 0 + + inScope := false + enteringScope := false + indentLevels := 0 + line := 0 + + externCDecl := removeSpacesAndTabs(EXTERN) + + for scanner.Scan() { + line++ + str := removeSpacesAndTabs(scanner.Text()) + + if len(str) == 0 { + continue + } + + // check if we are on the first non empty line after externCDecl in case 3 + if enteringScope == true { + enteringScope = false + } + + // check if the line contains externCDecl + if strings.Contains(str, externCDecl) { + inScope = true + if len(str) == len(externCDecl) { + // case 3 + enteringScope = true + } + } + if inScope == true { + lines[tag.Filename] = append(lines[tag.Filename], line) + } + indentLevels += strings.Count(str, "{") - strings.Count(str, "}") + + // Bail out if indentLevel is zero and we are not in case 3 + if indentLevels == 0 && enteringScope == false { + inScope = false + } + } + } + + } + return lines +} diff --git a/src/arduino.cc/builder/ctags_runner.go b/src/arduino.cc/builder/ctags_runner.go index b869d6f3..76dbb5bb 100644 --- a/src/arduino.cc/builder/ctags_runner.go +++ b/src/arduino.cc/builder/ctags_runner.go @@ -75,6 +75,8 @@ func (s *CTagsRunner) Run(ctx *types.Context) error { parser := &ctags.CTagsParser{} ctx.CTagsOfPreprocessedSource = parser.Parse(ctx.CTagsOutput) + + parser.FixCLinkageTagsDeclarations(ctx.CTagsOfPreprocessedSource) protos, line := parser.GeneratePrototypes() if line != -1 { ctx.PrototypesLineWhereToInsert = line From 08606b3c2697adc6f3dc7f33f6346d42ffe10993 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 29 Nov 2016 15:06:00 +0100 Subject: [PATCH 03/11] Move prototypeAndCodeDontMatch to ctags_has_issues --- .../builder/ctags/ctags_has_issues.go | 41 +++++++++++++++++ src/arduino.cc/builder/ctags/ctags_parser.go | 45 +------------------ 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/src/arduino.cc/builder/ctags/ctags_has_issues.go b/src/arduino.cc/builder/ctags/ctags_has_issues.go index 4d4750af..6757ebe1 100644 --- a/src/arduino.cc/builder/ctags/ctags_has_issues.go +++ b/src/arduino.cc/builder/ctags/ctags_has_issues.go @@ -58,6 +58,47 @@ func sliceContainsInt(s []int, e int) bool { return false } +func (p *CTagsParser) prototypeAndCodeDontMatch(tag *types.CTag) bool { + if tag.SkipMe { + return true + } + + code := removeSpacesAndTabs(tag.Code) + + // original code is multi-line, which tags doesn't have - could we find this code in the + // original source file, for purposes of checking here? + if strings.Index(code, ")") == -1 { + file, err := os.Open(tag.Filename) + if err == nil { + defer file.Close() + + scanner := bufio.NewScanner(file) + line := 1 + + // skip lines until we get to the start of this tag + for scanner.Scan() && line < tag.Line { + line++ + } + + // read up to 10 lines in search of a closing paren + newcode := scanner.Text() + for scanner.Scan() && line < (tag.Line+10) && strings.Index(newcode, ")") == -1 { + newcode += scanner.Text() + } + + // don't bother replacing the code text if we haven't found a closing paren + if strings.Index(newcode, ")") != -1 { + code = removeSpacesAndTabs(newcode) + } + } + } + + prototype := removeSpacesAndTabs(tag.Prototype) + prototype = removeTralingSemicolon(prototype) + + return strings.Index(code, prototype) == -1 +} + /* This function scans the source files searching for "extern C" context * It save the line numbers in a map filename -> {lines...} */ diff --git a/src/arduino.cc/builder/ctags/ctags_parser.go b/src/arduino.cc/builder/ctags/ctags_parser.go index c630400d..3114041b 100644 --- a/src/arduino.cc/builder/ctags/ctags_parser.go +++ b/src/arduino.cc/builder/ctags/ctags_parser.go @@ -30,8 +30,6 @@ package ctags import ( - "bufio" - "os" "strconv" "strings" @@ -69,7 +67,7 @@ func (p *CTagsParser) Parse(ctagsOutput string) []*types.CTag { p.addPrototypes() p.removeDefinedProtypes() p.skipDuplicates() - p.skipTagsWhere(prototypeAndCodeDontMatch) + p.skipTagsWhere(p.prototypeAndCodeDontMatch) return p.tags } @@ -148,47 +146,6 @@ func (p *CTagsParser) skipTagsWhere(skipFunc skipFuncType) { } } -func prototypeAndCodeDontMatch(tag *types.CTag) bool { - if tag.SkipMe { - return true - } - - code := removeSpacesAndTabs(tag.Code) - - // original code is multi-line, which tags doesn't have - could we find this code in the - // original source file, for purposes of checking here? - if strings.Index(code, ")") == -1 { - file, err := os.Open(tag.Filename) - if err == nil { - defer file.Close() - - scanner := bufio.NewScanner(file) - line := 1 - - // skip lines until we get to the start of this tag - for scanner.Scan() && line < tag.Line { - line++ - } - - // read up to 10 lines in search of a closing paren - newcode := scanner.Text() - for scanner.Scan() && line < (tag.Line+10) && strings.Index(newcode, ")") == -1 { - newcode += scanner.Text() - } - - // don't bother replacing the code text if we haven't found a closing paren - if strings.Index(newcode, ")") != -1 { - code = removeSpacesAndTabs(newcode) - } - } - } - - prototype := removeSpacesAndTabs(tag.Prototype) - prototype = removeTralingSemicolon(prototype) - - return strings.Index(code, prototype) == -1 -} - func removeTralingSemicolon(s string) string { return s[0 : len(s)-1] } From aad75340a17f6f63013b3f2b5d669d9343efdca1 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 29 Nov 2016 15:23:53 +0100 Subject: [PATCH 04/11] Add additional test for difficult multiline prototypes (whitespaces are intended) --- .../sketch_with_multiline_prototypes.ino | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/arduino.cc/builder/test/sketch_with_multiline_prototypes/sketch_with_multiline_prototypes.ino b/src/arduino.cc/builder/test/sketch_with_multiline_prototypes/sketch_with_multiline_prototypes.ino index a4fdf58a..e0e0d2e0 100644 --- a/src/arduino.cc/builder/test/sketch_with_multiline_prototypes/sketch_with_multiline_prototypes.ino +++ b/src/arduino.cc/builder/test/sketch_with_multiline_prototypes/sketch_with_multiline_prototypes.ino @@ -1,4 +1,12 @@ -void setup() { myctagstestfunc(1,2,3,4); } +void setup() { + myctagstestfunc(1,2,3,4); + test(); + test3(); + test5(1,2,3); + test7(); + test8(); + test9(42, 42); +} void myctagstestfunc(int a, int b, @@ -7,3 +15,33 @@ int d) { } void loop() {} +void +test() {} + +void +// comment +test3() {} + +void +test5(int a, + int b, + int c) +{ + +} + +void /* comment */ +test7() {} + +void +/* +multi +line +comment +*/ +test8() {} + + void + /* comment */ + test9(int a, + int b) {} \ No newline at end of file From 396b2a00b4cf57ea68025a2719994158346032d0 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 29 Nov 2016 15:26:50 +0100 Subject: [PATCH 05/11] Fix multiline prototype issue This time it should also work with all the functions with return type and signature on different lines --- .../builder/ctags/ctags_has_issues.go | 85 ++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/src/arduino.cc/builder/ctags/ctags_has_issues.go b/src/arduino.cc/builder/ctags/ctags_has_issues.go index 6757ebe1..826857a9 100644 --- a/src/arduino.cc/builder/ctags/ctags_has_issues.go +++ b/src/arduino.cc/builder/ctags/ctags_has_issues.go @@ -96,7 +96,90 @@ func (p *CTagsParser) prototypeAndCodeDontMatch(tag *types.CTag) bool { prototype := removeSpacesAndTabs(tag.Prototype) prototype = removeTralingSemicolon(prototype) - return strings.Index(code, prototype) == -1 + // Prototype matches exactly with the code? + ret := strings.Index(code, prototype) + + if ret == -1 { + // If the definition is multiline ctags uses the function name as line number + // Try to match functions in the form + // void + // foo() {} + + // Add to code n non-whitespace non-comments tokens before the code line + + code = removeEverythingAfterClosingRoundBracket(code) + // Get how many characters are "missing" + n := strings.Index(prototype, code) + // Add these characters to "code" string + code = getFunctionProtoWithNPreviousCharacters(tag, code, n) + // Check again for perfect matching + ret = strings.Index(code, prototype) + } + + return ret == -1 +} + +func removeEverythingAfterClosingRoundBracket(s string) string { + n := strings.Index(s, ")") + return s[0 : n+1] +} + +func getFunctionProtoWithNPreviousCharacters(tag *types.CTag, code string, n int) string { + + /* FIXME I'm ugly */ + expectedPrototypeLen := len(code) + n + + file, err := os.Open(tag.Filename) + if err == nil { + defer file.Close() + + scanner := bufio.NewScanner(file) + line := 0 + multilinecomment := false + var textBuffer []string + + // buffer lines until we get to the start of this tag + for scanner.Scan() && line < (tag.Line-1) { + line++ + text := scanner.Text() + textBuffer = append(textBuffer, text) + } + + for line > 0 && len(code) < expectedPrototypeLen { + + line = line - 1 + text := textBuffer[line] + + // Remove C++ style comments + if strings.Index(text, "//") != -1 { + text = text[0:strings.Index(text, "//")] + } + + // Remove C style comments + if strings.Index(text, "*/") != -1 { + if strings.Index(text, "/*") != -1 { + // C style comments on the same line + text = text[0:strings.Index(text, "/*")] + text[strings.Index(text, "*/")+1:len(text)-1] + } else { + text = text[strings.Index(text, "*/")+1 : len(text)-1] + multilinecomment = true + } + } + + if multilinecomment { + if strings.Index(text, "/*") != -1 { + text = text[0:strings.Index(text, "/*")] + multilinecomment = false + } else { + text = "" + } + } + + code = text + code + code = removeSpacesAndTabs(code) + } + } + return code } /* This function scans the source files searching for "extern C" context From db670f66bf2f800342f5561e2d467c22c75cb769 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 29 Nov 2016 15:30:30 +0100 Subject: [PATCH 06/11] Add additional test for multiline with comments --- .../sketch_with_multiline_prototypes.ino | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/arduino.cc/builder/test/sketch_with_multiline_prototypes/sketch_with_multiline_prototypes.ino b/src/arduino.cc/builder/test/sketch_with_multiline_prototypes/sketch_with_multiline_prototypes.ino index e0e0d2e0..85f7fb6d 100644 --- a/src/arduino.cc/builder/test/sketch_with_multiline_prototypes/sketch_with_multiline_prototypes.ino +++ b/src/arduino.cc/builder/test/sketch_with_multiline_prototypes/sketch_with_multiline_prototypes.ino @@ -6,6 +6,7 @@ void setup() { test7(); test8(); test9(42, 42); + test10(0,0,0); } void myctagstestfunc(int a, @@ -44,4 +45,9 @@ test8() {} void /* comment */ test9(int a, - int b) {} \ No newline at end of file + int b) {} + +void test10(int a, // this + int b, // doesn't + int c // work + ) {} \ No newline at end of file From 75a2be8d63b23fdf2dc7d428e248e7c0913507bf Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 29 Nov 2016 15:50:44 +0100 Subject: [PATCH 07/11] Make forward multiline search comments-aware --- .../builder/ctags/ctags_has_issues.go | 71 ++++++++++--------- src/arduino.cc/builder/ctags/ctags_parser.go | 6 +- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/src/arduino.cc/builder/ctags/ctags_has_issues.go b/src/arduino.cc/builder/ctags/ctags_has_issues.go index 826857a9..b5c4bee0 100644 --- a/src/arduino.cc/builder/ctags/ctags_has_issues.go +++ b/src/arduino.cc/builder/ctags/ctags_has_issues.go @@ -65,9 +65,8 @@ func (p *CTagsParser) prototypeAndCodeDontMatch(tag *types.CTag) bool { code := removeSpacesAndTabs(tag.Code) - // original code is multi-line, which tags doesn't have - could we find this code in the - // original source file, for purposes of checking here? if strings.Index(code, ")") == -1 { + // Add to code non-whitespace non-comments tokens until we find a closing round bracket file, err := os.Open(tag.Filename) if err == nil { defer file.Close() @@ -81,18 +80,19 @@ func (p *CTagsParser) prototypeAndCodeDontMatch(tag *types.CTag) bool { } // read up to 10 lines in search of a closing paren - newcode := scanner.Text() - for scanner.Scan() && line < (tag.Line+10) && strings.Index(newcode, ")") == -1 { - newcode += scanner.Text() - } + multilinecomment := false + temp := "" - // don't bother replacing the code text if we haven't found a closing paren - if strings.Index(newcode, ")") != -1 { - code = removeSpacesAndTabs(newcode) + code, multilinecomment = removeComments(scanner.Text(), multilinecomment) + for scanner.Scan() && line < (tag.Line+10) && strings.Index(temp, ")") == -1 { + temp, multilinecomment = removeComments(scanner.Text(), multilinecomment) + code += temp } } } + code = removeSpacesAndTabs(code) + prototype := removeSpacesAndTabs(tag.Prototype) prototype = removeTralingSemicolon(prototype) @@ -150,30 +150,7 @@ func getFunctionProtoWithNPreviousCharacters(tag *types.CTag, code string, n int line = line - 1 text := textBuffer[line] - // Remove C++ style comments - if strings.Index(text, "//") != -1 { - text = text[0:strings.Index(text, "//")] - } - - // Remove C style comments - if strings.Index(text, "*/") != -1 { - if strings.Index(text, "/*") != -1 { - // C style comments on the same line - text = text[0:strings.Index(text, "/*")] + text[strings.Index(text, "*/")+1:len(text)-1] - } else { - text = text[strings.Index(text, "*/")+1 : len(text)-1] - multilinecomment = true - } - } - - if multilinecomment { - if strings.Index(text, "/*") != -1 { - text = text[0:strings.Index(text, "/*")] - multilinecomment = false - } else { - text = "" - } - } + text, multilinecomment = removeComments(text, multilinecomment) code = text + code code = removeSpacesAndTabs(code) @@ -182,6 +159,34 @@ func getFunctionProtoWithNPreviousCharacters(tag *types.CTag, code string, n int return code } +func removeComments(text string, multilinecomment bool) (string, bool) { + // Remove C++ style comments + if strings.Index(text, "//") != -1 { + text = text[0:strings.Index(text, "//")] + } + + // Remove C style comments + if strings.Index(text, "*/") != -1 { + if strings.Index(text, "/*") != -1 { + // C style comments on the same line + text = text[0:strings.Index(text, "/*")] + text[strings.Index(text, "*/")+1:len(text)-1] + } else { + text = text[strings.Index(text, "*/")+1 : len(text)-1] + multilinecomment = true + } + } + + if multilinecomment { + if strings.Index(text, "/*") != -1 { + text = text[0:strings.Index(text, "/*")] + multilinecomment = false + } else { + text = "" + } + } + return text, multilinecomment +} + /* This function scans the source files searching for "extern C" context * It save the line numbers in a map filename -> {lines...} */ diff --git a/src/arduino.cc/builder/ctags/ctags_parser.go b/src/arduino.cc/builder/ctags/ctags_parser.go index 3114041b..5d0e07a7 100644 --- a/src/arduino.cc/builder/ctags/ctags_parser.go +++ b/src/arduino.cc/builder/ctags/ctags_parser.go @@ -96,9 +96,9 @@ func addPrototype(tag *types.CTag) { if strings.Index(tag.Code, STATIC+" ") != -1 { tag.PrototypeModifiers = tag.PrototypeModifiers + " " + STATIC } - if strings.Index(tag.Code, EXTERN+" ") != -1 { - tag.PrototypeModifiers = tag.PrototypeModifiers + " " + EXTERN - } + + // Extern "C" modifier is now added in FixCLinkageTagsDeclarations + tag.PrototypeModifiers = strings.TrimSpace(tag.PrototypeModifiers) } From a7fe1190327c1e6afdf66c67f1c58da0a5ce39cd Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 2 Dec 2016 18:29:35 +0100 Subject: [PATCH 08/11] Fix multiline templates prototype generation --- .../builder/ctags/ctags_has_issues.go | 50 +++++++++++++++++-- src/arduino.cc/builder/ctags/ctags_parser.go | 18 ++++--- 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/arduino.cc/builder/ctags/ctags_has_issues.go b/src/arduino.cc/builder/ctags/ctags_has_issues.go index b5c4bee0..1005e419 100644 --- a/src/arduino.cc/builder/ctags/ctags_has_issues.go +++ b/src/arduino.cc/builder/ctags/ctags_has_issues.go @@ -110,31 +110,73 @@ func (p *CTagsParser) prototypeAndCodeDontMatch(tag *types.CTag) bool { code = removeEverythingAfterClosingRoundBracket(code) // Get how many characters are "missing" n := strings.Index(prototype, code) + line := 0 // Add these characters to "code" string - code = getFunctionProtoWithNPreviousCharacters(tag, code, n) + code, line = getFunctionProtoWithNPreviousCharacters(tag, code, n) // Check again for perfect matching ret = strings.Index(code, prototype) + if ret != -1 { + tag.Line = line + } } return ret == -1 } +func findTemplateMultiline(tag *types.CTag) string { + code, _ := getFunctionProtoUntilTemplateToken(tag, tag.Code) + return removeEverythingAfterClosingRoundBracket(code) +} + func removeEverythingAfterClosingRoundBracket(s string) string { n := strings.Index(s, ")") return s[0 : n+1] } -func getFunctionProtoWithNPreviousCharacters(tag *types.CTag, code string, n int) string { +func getFunctionProtoUntilTemplateToken(tag *types.CTag, code string) (string, int) { + + /* FIXME I'm ugly */ + line := 0 + + file, err := os.Open(tag.Filename) + if err == nil { + defer file.Close() + + scanner := bufio.NewScanner(file) + multilinecomment := false + var textBuffer []string + + // buffer lines until we get to the start of this tag + for scanner.Scan() && line < (tag.Line-1) { + line++ + text := scanner.Text() + textBuffer = append(textBuffer, text) + } + + for line > 0 && !strings.Contains(code, TEMPLATE) { + + line = line - 1 + text := textBuffer[line] + + text, multilinecomment = removeComments(text, multilinecomment) + + code = text + code + } + } + return code, line +} + +func getFunctionProtoWithNPreviousCharacters(tag *types.CTag, code string, n int) (string, int) { /* FIXME I'm ugly */ expectedPrototypeLen := len(code) + n + line := 0 file, err := os.Open(tag.Filename) if err == nil { defer file.Close() scanner := bufio.NewScanner(file) - line := 0 multilinecomment := false var textBuffer []string @@ -156,7 +198,7 @@ func getFunctionProtoWithNPreviousCharacters(tag *types.CTag, code string, n int code = removeSpacesAndTabs(code) } } - return code + return code, line } func removeComments(text string, multilinecomment bool) (string, bool) { diff --git a/src/arduino.cc/builder/ctags/ctags_parser.go b/src/arduino.cc/builder/ctags/ctags_parser.go index 5d0e07a7..cb45e809 100644 --- a/src/arduino.cc/builder/ctags/ctags_parser.go +++ b/src/arduino.cc/builder/ctags/ctags_parser.go @@ -81,14 +81,20 @@ func (p *CTagsParser) addPrototypes() { } func addPrototype(tag *types.CTag) { - if strings.Index(tag.Prototype, TEMPLATE) == 0 || strings.Index(tag.Code, TEMPLATE) == 0 { - code := tag.Code - if strings.Contains(code, "{") { - code = code[:strings.Index(code, "{")] + if strings.Index(tag.Prototype, TEMPLATE) == 0 { + if strings.Index(tag.Code, TEMPLATE) == 0 { + code := tag.Code + if strings.Contains(code, "{") { + code = code[:strings.Index(code, "{")] + } else { + code = code[:strings.LastIndex(code, ")")+1] + } + tag.Prototype = code + ";" } else { - code = code[:strings.LastIndex(code, ")")+1] + //tag.Code is 99% multiline, recreate it + code := findTemplateMultiline(tag) + tag.Prototype = code + ";" } - tag.Prototype = code + ";" return } From ccba61a69361b562cd649f1d5295c11ddc20b022 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 7 Dec 2016 11:14:09 +0100 Subject: [PATCH 09/11] add test for multiline template --- .../sketch_with_multiline_template.ino | 10 ++++++++++ .../test/try_build_of_problematic_sketch_test.go | 4 ++++ 2 files changed, 14 insertions(+) create mode 100644 src/arduino.cc/builder/test/sketch_with_multiline_template/sketch_with_multiline_template.ino diff --git a/src/arduino.cc/builder/test/sketch_with_multiline_template/sketch_with_multiline_template.ino b/src/arduino.cc/builder/test/sketch_with_multiline_template/sketch_with_multiline_template.ino new file mode 100644 index 00000000..a3ddecdf --- /dev/null +++ b/src/arduino.cc/builder/test/sketch_with_multiline_template/sketch_with_multiline_template.ino @@ -0,0 +1,10 @@ +template< typename T > +T func(T t){ + return t * t; +} + +void setup() { + func( 12.34f ); +} + +void loop() {} diff --git a/src/arduino.cc/builder/test/try_build_of_problematic_sketch_test.go b/src/arduino.cc/builder/test/try_build_of_problematic_sketch_test.go index 0e0e7921..16be418e 100644 --- a/src/arduino.cc/builder/test/try_build_of_problematic_sketch_test.go +++ b/src/arduino.cc/builder/test/try_build_of_problematic_sketch_test.go @@ -209,6 +209,10 @@ func TestTryBuild040(t *testing.T) { tryBuild(t, "sketch_with_externC_multiline", "sketch_with_externC_multiline.ino") } +func TestTryBuild041(t *testing.T) { + tryBuild(t, "sketch_with_multiline_template", "sketch_with_multiline_template.ino") +} + func makeDefaultContext(t *testing.T) *types.Context { DownloadCoresAndToolsAndLibraries(t) From db95d14671b10cb096b10d1f4c80cea1fb05f27c Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 7 Dec 2016 11:16:13 +0100 Subject: [PATCH 10/11] Fix spurious "extern C" modifier generation Invalidating the first element of the slice works better than setting it to zero --- src/arduino.cc/builder/ctags/ctags_has_issues.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/arduino.cc/builder/ctags/ctags_has_issues.go b/src/arduino.cc/builder/ctags/ctags_has_issues.go index 1005e419..7049af64 100644 --- a/src/arduino.cc/builder/ctags/ctags_has_issues.go +++ b/src/arduino.cc/builder/ctags/ctags_has_issues.go @@ -246,7 +246,7 @@ func (p *CTagsParser) FindCLinkageLines(tags []*types.CTag) map[string][]int { if err == nil { defer file.Close() - lines[tag.Filename] = append(lines[tag.Filename], 0) + lines[tag.Filename] = append(lines[tag.Filename], -1) scanner := bufio.NewScanner(file) From bcd2800d94004f2a37549f0a82e953fd0e752f2e Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 14 Jun 2017 11:32:04 +0200 Subject: [PATCH 11/11] Fix merge conflict Self note: never use github conflict resolver again --- src/arduino.cc/builder/ctags_runner.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/arduino.cc/builder/ctags_runner.go b/src/arduino.cc/builder/ctags_runner.go index eae48bb3..2ab75638 100644 --- a/src/arduino.cc/builder/ctags_runner.go +++ b/src/arduino.cc/builder/ctags_runner.go @@ -74,10 +74,9 @@ func (s *CTagsRunner) Run(ctx *types.Context) error { ctx.CTagsOutput = string(sourceBytes) parser := &ctags.CTagsParser{} - ctx.CTagsOfPreprocessedSource = parser.Parse(ctx.CTagsOutput) - parser.FixCLinkageTagsDeclarations(ctx.CTagsOfPreprocessedSource) ctx.CTagsOfPreprocessedSource = parser.Parse(ctx.CTagsOutput, ctx.Sketch.MainFile.Name) + parser.FixCLinkageTagsDeclarations(ctx.CTagsOfPreprocessedSource) protos, line := parser.GeneratePrototypes() if line != -1 {