From 251ddaea30fee5809e6862820c471d0dd45ec92a Mon Sep 17 00:00:00 2001
From: Martino Facchin <m.facchin@arduino.cc>
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 <m.facchin@arduino.cc>
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 <m.facchin@arduino.cc>
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 <m.facchin@arduino.cc>
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 <m.facchin@arduino.cc>
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 <m.facchin@arduino.cc>
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 <m.facchin@arduino.cc>
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 <m.facchin@arduino.cc>
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 <m.facchin@arduino.cc>
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 <m.facchin@arduino.cc>
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 <m.facchin@arduino.cc>
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 {