From 5617f10414e5eda7521c89584c9319c0c018ed8d Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 19 May 2016 13:12:47 +0200 Subject: [PATCH 1/5] Properly escape filenames in #line directives In some cases, backslashes in a filenames were already escaped when emitting a #line directive, but sometimes no escaping happened. In any case, quotes in a filename should also be escaped. This commit introduces a helper function `QuoteCppString()` to handle quoting and escaping strings in a generic way. This new helper function is used in the code itself, but not in testcases yet. Note that ctags does not quite properly handle special characters and escaping in filenames. When parsing line markers, it just ignores escapes, so any escapes from the #line marker will be present in the ctags output as well. Before this commit, they would be copied unmodified into the #line directives generated from the ctags filenames. Now that these line directives have escaping applied, the filenames read from ctags need to be unescaped, to prevent double escapes. This unescaping already happened in CollectCTagsFromSketchFiles, but is now moved to parseTag where it is more appropriate and applies to all users. Since ctags does not handle escapes in line markers, it cuts the filename off at the first double quote. If a filename contains a double quote, the filename as output by ctags will be cut off before it, leaving the escaping \ at the end. Before this commit, this trailing \ would be output directly into the generated #line marker, which confused gcc and made the build fail. With this commit, this trailing \ is escaped itself, making the output syntactically valid, so the build now works with double quotes in a filename. However, due to this filename cut off, arduino-builder will no longer generate prototypes for functions in files that have a double quote in them (since it can no longer detect them as sketch functions), so double quotes (and probably tabs too) in filenames are still not completely supported yet. Signed-off-by: Matthijs Kooijman --- .../builder/collect_ctags_from_sketch_files.go | 3 +-- src/arduino.cc/builder/ctags/ctags_parser.go | 9 ++++++++- src/arduino.cc/builder/prototypes_adder.go | 5 +++-- src/arduino.cc/builder/sketch_source_merger.go | 6 +++--- src/arduino.cc/builder/test/utils_test.go | 13 +++++++++++++ src/arduino.cc/builder/utils/utils.go | 9 +++++++++ 6 files changed, 37 insertions(+), 8 deletions(-) diff --git a/src/arduino.cc/builder/collect_ctags_from_sketch_files.go b/src/arduino.cc/builder/collect_ctags_from_sketch_files.go index aee6935c..979aba9f 100644 --- a/src/arduino.cc/builder/collect_ctags_from_sketch_files.go +++ b/src/arduino.cc/builder/collect_ctags_from_sketch_files.go @@ -32,7 +32,6 @@ package builder import ( "arduino.cc/builder/types" "arduino.cc/builder/utils" - "strings" ) type CollectCTagsFromSketchFiles struct{} @@ -43,7 +42,7 @@ func (s *CollectCTagsFromSketchFiles) Run(ctx *types.Context) error { allCtags := ctx.CTagsOfPreprocessedSource ctagsOfSketch := []*types.CTag{} for _, ctag := range allCtags { - if utils.SliceContains(sketchFileNames, strings.Replace(ctag.Filename, "\\\\", "\\", -1)) { + if utils.SliceContains(sketchFileNames, ctag.Filename) { ctagsOfSketch = append(ctagsOfSketch, ctag) } } diff --git a/src/arduino.cc/builder/ctags/ctags_parser.go b/src/arduino.cc/builder/ctags/ctags_parser.go index 55497155..3926c8ff 100644 --- a/src/arduino.cc/builder/ctags/ctags_parser.go +++ b/src/arduino.cc/builder/ctags/ctags_parser.go @@ -229,7 +229,14 @@ func parseTag(row string) *types.CTag { parts := strings.Split(row, "\t") tag.FunctionName = parts[0] - tag.Filename = parts[1] + // This unescapes any backslashes in the filename. These + // filenames that ctags outputs originate from the line markers + // in the source, as generated by gcc. gcc escapes both + // backslashes and double quotes, but ctags ignores any escaping + // and just cuts off the filename at the first double quote it + // sees. This means any backslashes are still escaped, and need + // to be unescape, and any quotes will just break the build. + tag.Filename = strings.Replace(parts[1], "\\\\", "\\", -1) parts = parts[2:] diff --git a/src/arduino.cc/builder/prototypes_adder.go b/src/arduino.cc/builder/prototypes_adder.go index 9e05b109..d80b656d 100644 --- a/src/arduino.cc/builder/prototypes_adder.go +++ b/src/arduino.cc/builder/prototypes_adder.go @@ -32,6 +32,7 @@ package builder import ( "arduino.cc/builder/constants" "arduino.cc/builder/types" + "arduino.cc/builder/utils" "fmt" "strconv" "strings" @@ -87,7 +88,7 @@ func composePrototypeSection(line int, prototypes []*types.Prototype) string { str := joinPrototypes(prototypes) str += "\n#line " str += strconv.Itoa(line) - str += " \"" + prototypes[0].File + "\"" + str += " " + utils.QuoteCppString(prototypes[0].File) str += "\n" return str @@ -99,7 +100,7 @@ func joinPrototypes(prototypes []*types.Prototype) string { if signatureContainsaDefaultArg(proto) { continue } - prototypesSlice = append(prototypesSlice, "#line "+strconv.Itoa(proto.Line)+" \""+proto.File+"\"") + prototypesSlice = append(prototypesSlice, "#line "+strconv.Itoa(proto.Line)+" "+utils.QuoteCppString(proto.File)) prototypeParts := []string{} if proto.Modifiers != "" { prototypeParts = append(prototypeParts, proto.Modifiers) diff --git a/src/arduino.cc/builder/sketch_source_merger.go b/src/arduino.cc/builder/sketch_source_merger.go index 36c7c7a9..3ad088db 100644 --- a/src/arduino.cc/builder/sketch_source_merger.go +++ b/src/arduino.cc/builder/sketch_source_merger.go @@ -32,8 +32,8 @@ package builder import ( "arduino.cc/builder/types" + "arduino.cc/builder/utils" "regexp" - "strings" ) type SketchSourceMerger struct{} @@ -47,7 +47,7 @@ func (s *SketchSourceMerger) Run(ctx *types.Context) error { includeSection += "#include \n" lineOffset++ } - includeSection += "#line 1 \"" + strings.Replace((&sketch.MainFile).Name, "\\", "\\\\", -1) + "\"\n" + includeSection += "#line 1 " + utils.QuoteCppString(sketch.MainFile.Name) + "\n" lineOffset++ ctx.IncludeSection = includeSection @@ -73,7 +73,7 @@ func sketchIncludesArduinoH(sketch *types.SketchFile) bool { } func addSourceWrappedWithLineDirective(sketch *types.SketchFile) string { - source := "#line 1 \"" + strings.Replace(sketch.Name, "\\", "\\\\", -1) + "\"\n" + source := "#line 1 " + utils.QuoteCppString(sketch.Name) + "\n" source += sketch.Source source += "\n" diff --git a/src/arduino.cc/builder/test/utils_test.go b/src/arduino.cc/builder/test/utils_test.go index c3662e1d..3ff5610d 100644 --- a/src/arduino.cc/builder/test/utils_test.go +++ b/src/arduino.cc/builder/test/utils_test.go @@ -87,3 +87,16 @@ func TestMapTrimSpace(t *testing.T) { require.Equal(t, "how are", parts[2]) require.Equal(t, "you?", parts[3]) } + +func TestQuoteCppString(t *testing.T) { + cases := map[string]string { + `foo`: `"foo"`, + `foo\bar`: `"foo\\bar"`, + `foo "is" quoted and \\bar"" escaped\`: `"foo \"is\" quoted and \\\\bar\"\" escaped\\"`, + // ASCII 0x20 - 0x7e, excluding ` + ` !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~`: `" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~"`, + } + for input, expected := range cases { + require.Equal(t, expected, utils.QuoteCppString(input)) + } +} diff --git a/src/arduino.cc/builder/utils/utils.go b/src/arduino.cc/builder/utils/utils.go index ad8d50e5..84dd854e 100644 --- a/src/arduino.cc/builder/utils/utils.go +++ b/src/arduino.cc/builder/utils/utils.go @@ -407,3 +407,12 @@ func LogIfVerbose(level string, format string, args ...interface{}) types.Comman func LogThis(level string, format string, args ...interface{}) types.Command { return &loggerAction{false, level, format, args} } + +// Returns the given string as a quoted string for use with the C +// preprocessor. This adds double quotes around it and escapes any +// double quotes and backslashes in the string. +func QuoteCppString(str string) string { + str = strings.Replace(str, "\\", "\\\\", -1) + str = strings.Replace(str, "\"", "\\\"", -1) + return "\"" + str + "\"" +} From a2eb033b7035164d9b7aec3bb027295bae61d781 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 19 May 2016 14:35:58 +0200 Subject: [PATCH 2/5] Use QuoteCppString in testcases In some testcases, the generated output is checked, which contains #line directives with filenames. Previously, this compared against the filename with just backslashes escaped, but this would be incorrect when there are quotes in the filename. Since the previous commit, the arduino-builder code itself uses a new `utils.QuoteCppString` function to apply escaping, so this applies that same escaping. Part of the changes modify the test code directly, part of the changes replace the `EscapeBackSlashes` "function" that was used in interpolating some text files with a call to QuoteCppString instead. Note that QuoteCppString already adds quotes around its result, so those are removed from the places where this function is called. Signed-off-by: Matthijs Kooijman --- src/arduino.cc/builder/test/helper.go | 6 +- .../builder/test/prototypes_adder_test.go | 73 ++++++++++--------- .../builder/test/sketch1/merged_sketch.txt | 8 +- .../sketch2/SketchWithIfDef.preprocessed.txt | 16 ++-- .../SketchWithIfDef.resolved.directives.txt | 4 +- .../test/sketch3/Baladuino.preprocessed.txt | 10 +-- ...harWithEscapedDoubleQuote.preprocessed.txt | 48 ++++++------ ...deBetweenMultilineComment.preprocessed.txt | 10 +-- .../LineContinuations.preprocessed.txt | 10 +-- .../StringWithComment.preprocessed.txt | 10 +-- .../sketch8/SketchWithStruct.preprocessed.txt | 12 +-- .../sketch_with_config.preprocessed.txt | 10 +-- .../sketch.preprocessed.SAM.txt | 16 ++-- .../sketch_with_ifdef/sketch.preprocessed.txt | 16 ++-- 14 files changed, 124 insertions(+), 125 deletions(-) diff --git a/src/arduino.cc/builder/test/helper.go b/src/arduino.cc/builder/test/helper.go index 3e83d337..cd6e2b3d 100644 --- a/src/arduino.cc/builder/test/helper.go +++ b/src/arduino.cc/builder/test/helper.go @@ -33,22 +33,20 @@ package test import ( "arduino.cc/builder/constants" "arduino.cc/builder/types" + "arduino.cc/builder/utils" "bytes" "fmt" "github.com/go-errors/errors" "github.com/stretchr/testify/assert" "io/ioutil" "path/filepath" - "strings" "testing" "text/template" ) func LoadAndInterpolate(t *testing.T, filename string, ctx *types.Context) string { funcsMap := template.FuncMap{ - "EscapeBackSlashes": func(s string) string { - return strings.Replace(s, "\\", "\\\\", -1) - }, + "QuoteCppString": utils.QuoteCppString, } tpl, err := template.New(filepath.Base(filename)).Funcs(funcsMap).ParseFiles(filename) diff --git a/src/arduino.cc/builder/test/prototypes_adder_test.go b/src/arduino.cc/builder/test/prototypes_adder_test.go index a6f46a45..db33a440 100644 --- a/src/arduino.cc/builder/test/prototypes_adder_test.go +++ b/src/arduino.cc/builder/test/prototypes_adder_test.go @@ -33,6 +33,7 @@ package test import ( "arduino.cc/builder" "arduino.cc/builder/types" + "arduino.cc/builder/utils" "github.com/stretchr/testify/require" "os" "path/filepath" @@ -44,7 +45,7 @@ func TestPrototypesAdderBridgeExample(t *testing.T) { DownloadCoresAndToolsAndLibraries(t) sketchLocation := filepath.Join("downloaded_libraries", "Bridge", "examples", "Bridge", "Bridge.ino") - absoluteSketchLocation := strings.Replace(Abs(t, sketchLocation), "\\", "\\\\", -1) + quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation)) ctx := &types.Context{ HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"}, @@ -81,8 +82,8 @@ func TestPrototypesAdderBridgeExample(t *testing.T) { NoError(t, err) } - require.Equal(t, "#include \n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.IncludeSection) - require.Equal(t, "#line 33 \""+absoluteSketchLocation+"\"\nvoid setup();\n#line 46 \""+absoluteSketchLocation+"\"\nvoid loop();\n#line 62 \""+absoluteSketchLocation+"\"\nvoid process(BridgeClient client);\n#line 82 \""+absoluteSketchLocation+"\"\nvoid digitalCommand(BridgeClient client);\n#line 109 \""+absoluteSketchLocation+"\"\nvoid analogCommand(BridgeClient client);\n#line 149 \""+absoluteSketchLocation+"\"\nvoid modeCommand(BridgeClient client);\n#line 33 \""+absoluteSketchLocation+"\"\n", ctx.PrototypesSection) + require.Equal(t, "#include \n#line 1 " + quotedSketchLocation + "\n", ctx.IncludeSection) + require.Equal(t, "#line 33 "+quotedSketchLocation+"\nvoid setup();\n#line 46 "+quotedSketchLocation+"\nvoid loop();\n#line 62 "+quotedSketchLocation+"\nvoid process(BridgeClient client);\n#line 82 "+quotedSketchLocation+"\nvoid digitalCommand(BridgeClient client);\n#line 109 "+quotedSketchLocation+"\nvoid analogCommand(BridgeClient client);\n#line 149 "+quotedSketchLocation+"\nvoid modeCommand(BridgeClient client);\n#line 33 "+quotedSketchLocation+"\n", ctx.PrototypesSection) } func TestPrototypesAdderSketchWithIfDef(t *testing.T) { @@ -374,7 +375,7 @@ func TestPrototypesAdderSketchWithConfig(t *testing.T) { DownloadCoresAndToolsAndLibraries(t) sketchLocation := filepath.Join("sketch_with_config", "sketch_with_config.ino") - absoluteSketchLocation := strings.Replace(Abs(t, sketchLocation), "\\", "\\\\", -1) + quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation)) ctx := &types.Context{ HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"}, @@ -409,8 +410,8 @@ func TestPrototypesAdderSketchWithConfig(t *testing.T) { NoError(t, err) } - require.Equal(t, "#include \n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.IncludeSection) - require.Equal(t, "#line 13 \""+absoluteSketchLocation+"\"\nvoid setup();\n#line 17 \""+absoluteSketchLocation+"\"\nvoid loop();\n#line 13 \""+absoluteSketchLocation+"\"\n", ctx.PrototypesSection) + require.Equal(t, "#include \n#line 1 "+quotedSketchLocation+"\n", ctx.IncludeSection) + require.Equal(t, "#line 13 "+quotedSketchLocation+"\nvoid setup();\n#line 17 "+quotedSketchLocation+"\nvoid loop();\n#line 13 "+quotedSketchLocation+"\n", ctx.PrototypesSection) preprocessed := LoadAndInterpolate(t, filepath.Join("sketch_with_config", "sketch_with_config.preprocessed.txt"), ctx) require.Equal(t, preprocessed, strings.Replace(ctx.Source, "\r\n", "\n", -1)) @@ -420,7 +421,7 @@ func TestPrototypesAdderSketchNoFunctionsTwoFiles(t *testing.T) { DownloadCoresAndToolsAndLibraries(t) sketchLocation := filepath.Join("sketch_no_functions_two_files", "main.ino") - absoluteSketchLocation := strings.Replace(Abs(t, sketchLocation), "\\", "\\\\", -1) + quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation)) ctx := &types.Context{ HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"}, @@ -455,7 +456,7 @@ func TestPrototypesAdderSketchNoFunctionsTwoFiles(t *testing.T) { NoError(t, err) } - require.Equal(t, "#include \n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.IncludeSection) + require.Equal(t, "#include \n#line 1 "+quotedSketchLocation+"\n", ctx.IncludeSection) require.Equal(t, "", ctx.PrototypesSection) } @@ -477,7 +478,7 @@ func TestPrototypesAdderSketchNoFunctions(t *testing.T) { defer os.RemoveAll(buildPath) sketchLocation := filepath.Join("sketch_no_functions", "main.ino") - absoluteSketchLocation := strings.Replace(Abs(t, sketchLocation), "\\", "\\\\", -1) + quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation)) commands := []types.Command{ @@ -498,7 +499,7 @@ func TestPrototypesAdderSketchNoFunctions(t *testing.T) { NoError(t, err) } - require.Equal(t, "#include \n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.IncludeSection) + require.Equal(t, "#include \n#line 1 "+quotedSketchLocation+"\n", ctx.IncludeSection) require.Equal(t, "", ctx.PrototypesSection) } @@ -506,7 +507,7 @@ func TestPrototypesAdderSketchWithDefaultArgs(t *testing.T) { DownloadCoresAndToolsAndLibraries(t) sketchLocation := filepath.Join("sketch_with_default_args", "sketch.ino") - absoluteSketchLocation := strings.Replace(Abs(t, sketchLocation), "\\", "\\\\", -1) + quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation)) ctx := &types.Context{ HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"}, @@ -541,15 +542,15 @@ func TestPrototypesAdderSketchWithDefaultArgs(t *testing.T) { NoError(t, err) } - require.Equal(t, "#include \n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.IncludeSection) - require.Equal(t, "#line 4 \""+absoluteSketchLocation+"\"\nvoid setup();\n#line 7 \""+absoluteSketchLocation+"\"\nvoid loop();\n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.PrototypesSection) + require.Equal(t, "#include \n#line 1 "+quotedSketchLocation+"\n", ctx.IncludeSection) + require.Equal(t, "#line 4 "+quotedSketchLocation+"\nvoid setup();\n#line 7 "+quotedSketchLocation+"\nvoid loop();\n#line 1 "+quotedSketchLocation+"\n", ctx.PrototypesSection) } func TestPrototypesAdderSketchWithInlineFunction(t *testing.T) { DownloadCoresAndToolsAndLibraries(t) sketchLocation := filepath.Join("sketch_with_inline_function", "sketch.ino") - absoluteSketchLocation := strings.Replace(Abs(t, sketchLocation), "\\", "\\\\", -1) + quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation)) ctx := &types.Context{ HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"}, @@ -584,9 +585,9 @@ func TestPrototypesAdderSketchWithInlineFunction(t *testing.T) { NoError(t, err) } - require.Equal(t, "#include \n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.IncludeSection) + require.Equal(t, "#include \n#line 1 "+quotedSketchLocation+"\n", ctx.IncludeSection) - expected := "#line 1 \"" + absoluteSketchLocation + "\"\nvoid setup();\n#line 2 \"" + absoluteSketchLocation + "\"\nvoid loop();\n#line 4 \"" + absoluteSketchLocation + "\"\nshort unsigned int testInt();\n#line 8 \"" + absoluteSketchLocation + "\"\nstatic int8_t testInline();\n#line 12 \"" + absoluteSketchLocation + "\"\n__attribute__((always_inline)) uint8_t testAttribute();\n#line 1 \"" + absoluteSketchLocation + "\"\n" + expected := "#line 1 " + quotedSketchLocation + "\nvoid setup();\n#line 2 " + quotedSketchLocation + "\nvoid loop();\n#line 4 " + quotedSketchLocation + "\nshort unsigned int testInt();\n#line 8 " + quotedSketchLocation + "\nstatic int8_t testInline();\n#line 12 " + quotedSketchLocation + "\n__attribute__((always_inline)) uint8_t testAttribute();\n#line 1 " + quotedSketchLocation + "\n" obtained := ctx.PrototypesSection // ctags based preprocessing removes "inline" but this is still OK // TODO: remove this exception when moving to a more powerful parser @@ -603,7 +604,7 @@ func TestPrototypesAdderSketchWithFunctionSignatureInsideIFDEF(t *testing.T) { DownloadCoresAndToolsAndLibraries(t) sketchLocation := filepath.Join("sketch_with_function_signature_inside_ifdef", "sketch.ino") - absoluteSketchLocation := strings.Replace(Abs(t, sketchLocation), "\\", "\\\\", -1) + quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation)) ctx := &types.Context{ HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"}, @@ -638,15 +639,15 @@ func TestPrototypesAdderSketchWithFunctionSignatureInsideIFDEF(t *testing.T) { NoError(t, err) } - require.Equal(t, "#include \n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.IncludeSection) - require.Equal(t, "#line 1 \""+absoluteSketchLocation+"\"\nvoid setup();\n#line 3 \""+absoluteSketchLocation+"\"\nvoid loop();\n#line 15 \""+absoluteSketchLocation+"\"\nint8_t adalight();\n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.PrototypesSection) + require.Equal(t, "#include \n#line 1 "+quotedSketchLocation+"\n", ctx.IncludeSection) + require.Equal(t, "#line 1 "+quotedSketchLocation+"\nvoid setup();\n#line 3 "+quotedSketchLocation+"\nvoid loop();\n#line 15 "+quotedSketchLocation+"\nint8_t adalight();\n#line 1 "+quotedSketchLocation+"\n", ctx.PrototypesSection) } func TestPrototypesAdderSketchWithUSBCON(t *testing.T) { DownloadCoresAndToolsAndLibraries(t) sketchLocation := filepath.Join("sketch_with_usbcon", "sketch.ino") - absoluteSketchLocation := strings.Replace(Abs(t, sketchLocation), "\\", "\\\\", -1) + quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation)) ctx := &types.Context{ HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"}, @@ -681,15 +682,15 @@ func TestPrototypesAdderSketchWithUSBCON(t *testing.T) { NoError(t, err) } - require.Equal(t, "#include \n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.IncludeSection) - require.Equal(t, "#line 5 \""+absoluteSketchLocation+"\"\nvoid ciao();\n#line 10 \""+absoluteSketchLocation+"\"\nvoid setup();\n#line 15 \""+absoluteSketchLocation+"\"\nvoid loop();\n#line 5 \""+absoluteSketchLocation+"\"\n", ctx.PrototypesSection) + require.Equal(t, "#include \n#line 1 "+quotedSketchLocation+"\n", ctx.IncludeSection) + require.Equal(t, "#line 5 "+quotedSketchLocation+"\nvoid ciao();\n#line 10 "+quotedSketchLocation+"\nvoid setup();\n#line 15 "+quotedSketchLocation+"\nvoid loop();\n#line 5 "+quotedSketchLocation+"\n", ctx.PrototypesSection) } func TestPrototypesAdderSketchWithTypename(t *testing.T) { DownloadCoresAndToolsAndLibraries(t) sketchLocation := filepath.Join("sketch_with_typename", "sketch.ino") - absoluteSketchLocation := strings.Replace(Abs(t, sketchLocation), "\\", "\\\\", -1) + quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation)) ctx := &types.Context{ HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"}, @@ -723,13 +724,13 @@ func TestPrototypesAdderSketchWithTypename(t *testing.T) { NoError(t, err) } - require.Equal(t, "#include \n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.IncludeSection) - expected := "#line 6 \"" + absoluteSketchLocation + "\"\nvoid setup();\n#line 10 \"" + absoluteSketchLocation + "\"\nvoid loop();\n#line 12 \"" + absoluteSketchLocation + "\"\ntypename Foo::Bar func();\n#line 6 \"" + absoluteSketchLocation + "\"\n" + require.Equal(t, "#include \n#line 1 "+quotedSketchLocation+"\n", ctx.IncludeSection) + expected := "#line 6 " + quotedSketchLocation + "\nvoid setup();\n#line 10 " + quotedSketchLocation + "\nvoid loop();\n#line 12 " + quotedSketchLocation + "\ntypename Foo::Bar func();\n#line 6 " + quotedSketchLocation + "\n" obtained := ctx.PrototypesSection // ctags based preprocessing ignores line with typename // TODO: remove this exception when moving to a more powerful parser - expected = strings.Replace(expected, "#line 12 \""+absoluteSketchLocation+"\"\ntypename Foo::Bar func();\n", "", -1) - obtained = strings.Replace(obtained, "#line 12 \""+absoluteSketchLocation+"\"\ntypename Foo::Bar func();\n", "", -1) + expected = strings.Replace(expected, "#line 12 "+quotedSketchLocation+"\ntypename Foo::Bar func();\n", "", -1) + obtained = strings.Replace(obtained, "#line 12 "+quotedSketchLocation+"\ntypename Foo::Bar func();\n", "", -1) require.Equal(t, expected, obtained) } @@ -737,7 +738,7 @@ func TestPrototypesAdderSketchWithIfDef2(t *testing.T) { DownloadCoresAndToolsAndLibraries(t) sketchLocation := filepath.Join("sketch_with_ifdef", "sketch.ino") - absoluteSketchLocation := strings.Replace(Abs(t, sketchLocation), "\\", "\\\\", -1) + quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation)) ctx := &types.Context{ HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"}, @@ -772,8 +773,8 @@ func TestPrototypesAdderSketchWithIfDef2(t *testing.T) { NoError(t, err) } - require.Equal(t, "#include \n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.IncludeSection) - require.Equal(t, "#line 5 \""+absoluteSketchLocation+"\"\nvoid elseBranch();\n#line 9 \""+absoluteSketchLocation+"\"\nvoid f1();\n#line 10 \""+absoluteSketchLocation+"\"\nvoid f2();\n#line 12 \""+absoluteSketchLocation+"\"\nvoid setup();\n#line 14 \""+absoluteSketchLocation+"\"\nvoid loop();\n#line 5 \""+absoluteSketchLocation+"\"\n", ctx.PrototypesSection) + require.Equal(t, "#include \n#line 1 "+quotedSketchLocation+"\n", ctx.IncludeSection) + require.Equal(t, "#line 5 "+quotedSketchLocation+"\nvoid elseBranch();\n#line 9 "+quotedSketchLocation+"\nvoid f1();\n#line 10 "+quotedSketchLocation+"\nvoid f2();\n#line 12 "+quotedSketchLocation+"\nvoid setup();\n#line 14 "+quotedSketchLocation+"\nvoid loop();\n#line 5 "+quotedSketchLocation+"\n", ctx.PrototypesSection) expectedSource := LoadAndInterpolate(t, filepath.Join("sketch_with_ifdef", "sketch.preprocessed.txt"), ctx) require.Equal(t, expectedSource, strings.Replace(ctx.Source, "\r\n", "\n", -1)) @@ -783,7 +784,7 @@ func TestPrototypesAdderSketchWithIfDef2SAM(t *testing.T) { DownloadCoresAndToolsAndLibraries(t) sketchLocation := filepath.Join("sketch_with_ifdef", "sketch.ino") - absoluteSketchLocation := strings.Replace(Abs(t, sketchLocation), "\\", "\\\\", -1) + quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation)) ctx := &types.Context{ HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"}, @@ -818,8 +819,8 @@ func TestPrototypesAdderSketchWithIfDef2SAM(t *testing.T) { NoError(t, err) } - require.Equal(t, "#include \n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.IncludeSection) - require.Equal(t, "#line 2 \""+absoluteSketchLocation+"\"\nvoid ifBranch();\n#line 9 \""+absoluteSketchLocation+"\"\nvoid f1();\n#line 10 \""+absoluteSketchLocation+"\"\nvoid f2();\n#line 12 \""+absoluteSketchLocation+"\"\nvoid setup();\n#line 14 \""+absoluteSketchLocation+"\"\nvoid loop();\n#line 2 \""+absoluteSketchLocation+"\"\n", ctx.PrototypesSection) + require.Equal(t, "#include \n#line 1 "+quotedSketchLocation+"\n", ctx.IncludeSection) + require.Equal(t, "#line 2 "+quotedSketchLocation+"\nvoid ifBranch();\n#line 9 "+quotedSketchLocation+"\nvoid f1();\n#line 10 "+quotedSketchLocation+"\nvoid f2();\n#line 12 "+quotedSketchLocation+"\nvoid setup();\n#line 14 "+quotedSketchLocation+"\nvoid loop();\n#line 2 "+quotedSketchLocation+"\n", ctx.PrototypesSection) expectedSource := LoadAndInterpolate(t, filepath.Join("sketch_with_ifdef", "sketch.preprocessed.SAM.txt"), ctx) require.Equal(t, expectedSource, strings.Replace(ctx.Source, "\r\n", "\n", -1)) @@ -829,7 +830,7 @@ func TestPrototypesAdderSketchWithConst(t *testing.T) { DownloadCoresAndToolsAndLibraries(t) sketchLocation := filepath.Join("sketch_with_const", "sketch.ino") - absoluteSketchLocation := strings.Replace(Abs(t, sketchLocation), "\\", "\\\\", -1) + quotedSketchLocation := utils.QuoteCppString(Abs(t, sketchLocation)) ctx := &types.Context{ HardwareFolders: []string{filepath.Join("..", "hardware"), "hardware", "downloaded_hardware"}, @@ -864,8 +865,8 @@ func TestPrototypesAdderSketchWithConst(t *testing.T) { NoError(t, err) } - require.Equal(t, "#include \n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.IncludeSection) - require.Equal(t, "#line 1 \""+absoluteSketchLocation+"\"\nvoid setup();\n#line 2 \""+absoluteSketchLocation+"\"\nvoid loop();\n#line 4 \""+absoluteSketchLocation+"\"\nconst __FlashStringHelper* test();\n#line 6 \""+absoluteSketchLocation+"\"\nconst int test3();\n#line 8 \""+absoluteSketchLocation+"\"\nvolatile __FlashStringHelper* test2();\n#line 10 \""+absoluteSketchLocation+"\"\nvolatile int test4();\n#line 1 \""+absoluteSketchLocation+"\"\n", ctx.PrototypesSection) + require.Equal(t, "#include \n#line 1 "+quotedSketchLocation+"\n", ctx.IncludeSection) + require.Equal(t, "#line 1 "+quotedSketchLocation+"\nvoid setup();\n#line 2 "+quotedSketchLocation+"\nvoid loop();\n#line 4 "+quotedSketchLocation+"\nconst __FlashStringHelper* test();\n#line 6 "+quotedSketchLocation+"\nconst int test3();\n#line 8 "+quotedSketchLocation+"\nvolatile __FlashStringHelper* test2();\n#line 10 "+quotedSketchLocation+"\nvolatile int test4();\n#line 1 "+quotedSketchLocation+"\n", ctx.PrototypesSection) } func TestPrototypesAdderSketchWithDosEol(t *testing.T) { diff --git a/src/arduino.cc/builder/test/sketch1/merged_sketch.txt b/src/arduino.cc/builder/test/sketch1/merged_sketch.txt index 38af5a38..4654de32 100644 --- a/src/arduino.cc/builder/test/sketch1/merged_sketch.txt +++ b/src/arduino.cc/builder/test/sketch1/merged_sketch.txt @@ -1,6 +1,6 @@ #include -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} void setup() { } @@ -8,9 +8,9 @@ void setup() { void loop() { } -#line 1 "{{EscapeBackSlashes (index .sketch.OtherSketchFiles 0).Name}}" +#line 1 {{QuoteCppString (index .sketch.OtherSketchFiles 0).Name}} -#line 1 "{{EscapeBackSlashes (index .sketch.OtherSketchFiles 1).Name}}" +#line 1 {{QuoteCppString (index .sketch.OtherSketchFiles 1).Name}} String hello() { return "world"; } diff --git a/src/arduino.cc/builder/test/sketch2/SketchWithIfDef.preprocessed.txt b/src/arduino.cc/builder/test/sketch2/SketchWithIfDef.preprocessed.txt index 4c7c7545..e0380e17 100644 --- a/src/arduino.cc/builder/test/sketch2/SketchWithIfDef.preprocessed.txt +++ b/src/arduino.cc/builder/test/sketch2/SketchWithIfDef.preprocessed.txt @@ -1,6 +1,6 @@ #include -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} #define DEBUG 1 #define DISABLED 0 @@ -16,17 +16,17 @@ typedef int MyType; #include "empty_2.h" -#line 16 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 16 {{QuoteCppString .sketch.MainFile.Name}} void setup(); -#line 21 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 21 {{QuoteCppString .sketch.MainFile.Name}} void loop(); -#line 33 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 33 {{QuoteCppString .sketch.MainFile.Name}} void debug(); -#line 44 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 44 {{QuoteCppString .sketch.MainFile.Name}} void disabledIsDefined(); -#line 48 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 48 {{QuoteCppString .sketch.MainFile.Name}} int useMyType(MyType type); -#line 16 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 16 {{QuoteCppString .sketch.MainFile.Name}} void setup() { // put your setup code here, to run once: diff --git a/src/arduino.cc/builder/test/sketch2/SketchWithIfDef.resolved.directives.txt b/src/arduino.cc/builder/test/sketch2/SketchWithIfDef.resolved.directives.txt index 1f7492ad..c01a6fe3 100644 --- a/src/arduino.cc/builder/test/sketch2/SketchWithIfDef.resolved.directives.txt +++ b/src/arduino.cc/builder/test/sketch2/SketchWithIfDef.resolved.directives.txt @@ -1,6 +1,6 @@ #include -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} #define DEBUG 1 #define DISABLED 0 diff --git a/src/arduino.cc/builder/test/sketch3/Baladuino.preprocessed.txt b/src/arduino.cc/builder/test/sketch3/Baladuino.preprocessed.txt index fd7fd313..989b6b9b 100644 --- a/src/arduino.cc/builder/test/sketch3/Baladuino.preprocessed.txt +++ b/src/arduino.cc/builder/test/sketch3/Baladuino.preprocessed.txt @@ -1,6 +1,6 @@ #include -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} /* * The code is released under the GNU General Public License. * Developed by Kristian Lauszus, TKJ Electronics 2013 @@ -88,11 +88,11 @@ WII Wii(&Btd); // The Wii library can communicate with Wiimotes and the Nunchuck // This can also be done using the Android or Processing application #endif -#line 88 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 88 {{QuoteCppString .sketch.MainFile.Name}} void setup(); -#line 204 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 204 {{QuoteCppString .sketch.MainFile.Name}} void loop(); -#line 88 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 88 {{QuoteCppString .sketch.MainFile.Name}} void setup() { /* Initialize UART */ Serial.begin(115200); diff --git a/src/arduino.cc/builder/test/sketch4/CharWithEscapedDoubleQuote.preprocessed.txt b/src/arduino.cc/builder/test/sketch4/CharWithEscapedDoubleQuote.preprocessed.txt index b7cfb728..62fa1ac6 100644 --- a/src/arduino.cc/builder/test/sketch4/CharWithEscapedDoubleQuote.preprocessed.txt +++ b/src/arduino.cc/builder/test/sketch4/CharWithEscapedDoubleQuote.preprocessed.txt @@ -1,6 +1,6 @@ #include -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} #include // required to send and receive AT commands from the GPRS Shield #include // required for I2C communication with the RTC @@ -39,49 +39,49 @@ Code Exclusively for GPRS shield: // Default set of instructions for GPRS Shield power control // -#line 39 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 39 {{QuoteCppString .sketch.MainFile.Name}} void setPowerStateTo( int newState ); -#line 64 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 64 {{QuoteCppString .sketch.MainFile.Name}} int getPowerState(); -#line 75 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 75 {{QuoteCppString .sketch.MainFile.Name}} void powerUpOrDown(); -#line 90 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 90 {{QuoteCppString .sketch.MainFile.Name}} void clearBufferArray(); -#line 96 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 96 {{QuoteCppString .sketch.MainFile.Name}} void makeMissedCall( char num[] ); -#line 111 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 111 {{QuoteCppString .sketch.MainFile.Name}} void sendTextMessage( char number[], char messg[] ); -#line 129 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 129 {{QuoteCppString .sketch.MainFile.Name}} void analise(byte incoming[], int length); -#line 179 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 179 {{QuoteCppString .sketch.MainFile.Name}} byte decToBcd( byte b ); -#line 184 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 184 {{QuoteCppString .sketch.MainFile.Name}} boolean getBit( byte addr, int pos ); -#line 190 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 190 {{QuoteCppString .sketch.MainFile.Name}} void setBit( byte addr, int pos, boolean newBit ); -#line 204 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 204 {{QuoteCppString .sketch.MainFile.Name}} byte getByte( byte addr ); -#line 213 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 213 {{QuoteCppString .sketch.MainFile.Name}} boolean getBytes( byte addr, int amount ); -#line 230 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 230 {{QuoteCppString .sketch.MainFile.Name}} void setByte( byte addr, byte newByte ); -#line 235 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 235 {{QuoteCppString .sketch.MainFile.Name}} void setBytes( byte addr, byte newBytes[], int amount ); -#line 244 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 244 {{QuoteCppString .sketch.MainFile.Name}} void getTime(); -#line 260 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 260 {{QuoteCppString .sketch.MainFile.Name}} void setTime( byte newTime[ 7 ] ); -#line 267 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 267 {{QuoteCppString .sketch.MainFile.Name}} void getRTCTemperature(); -#line 277 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 277 {{QuoteCppString .sketch.MainFile.Name}} void gprsListen(); -#line 294 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 294 {{QuoteCppString .sketch.MainFile.Name}} void printTime(); -#line 317 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 317 {{QuoteCppString .sketch.MainFile.Name}} void setup(); -#line 334 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 334 {{QuoteCppString .sketch.MainFile.Name}} void loop(); -#line 39 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 39 {{QuoteCppString .sketch.MainFile.Name}} void setPowerStateTo( int newState ) { if( newState != 1 && newState != 0 ) { // tests for an invalid state. In this case no change is made to powerstate diff --git a/src/arduino.cc/builder/test/sketch5/IncludeBetweenMultilineComment.preprocessed.txt b/src/arduino.cc/builder/test/sketch5/IncludeBetweenMultilineComment.preprocessed.txt index b29c05e7..a19528dc 100644 --- a/src/arduino.cc/builder/test/sketch5/IncludeBetweenMultilineComment.preprocessed.txt +++ b/src/arduino.cc/builder/test/sketch5/IncludeBetweenMultilineComment.preprocessed.txt @@ -1,16 +1,16 @@ #include -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} #include /* #include */ CapacitiveSensor cs_13_8 = CapacitiveSensor(13,8); -#line 6 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 6 {{QuoteCppString .sketch.MainFile.Name}} void setup(); -#line 10 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 10 {{QuoteCppString .sketch.MainFile.Name}} void loop(); -#line 6 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 6 {{QuoteCppString .sketch.MainFile.Name}} void setup() { Serial.begin(9600); diff --git a/src/arduino.cc/builder/test/sketch6/LineContinuations.preprocessed.txt b/src/arduino.cc/builder/test/sketch6/LineContinuations.preprocessed.txt index 567d373c..1d584583 100644 --- a/src/arduino.cc/builder/test/sketch6/LineContinuations.preprocessed.txt +++ b/src/arduino.cc/builder/test/sketch6/LineContinuations.preprocessed.txt @@ -1,17 +1,17 @@ #include -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} const char *foo = "\ hello \ world\n"; //" delete this comment line and the IDE parser will crash -#line 7 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 7 {{QuoteCppString .sketch.MainFile.Name}} void setup(); -#line 11 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 11 {{QuoteCppString .sketch.MainFile.Name}} void loop(); -#line 7 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 7 {{QuoteCppString .sketch.MainFile.Name}} void setup() { } diff --git a/src/arduino.cc/builder/test/sketch7/StringWithComment.preprocessed.txt b/src/arduino.cc/builder/test/sketch7/StringWithComment.preprocessed.txt index 9700f3e2..f4432fcd 100644 --- a/src/arduino.cc/builder/test/sketch7/StringWithComment.preprocessed.txt +++ b/src/arduino.cc/builder/test/sketch7/StringWithComment.preprocessed.txt @@ -1,11 +1,11 @@ #include -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} void setup(); -#line 10 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 10 {{QuoteCppString .sketch.MainFile.Name}} void loop(); -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} void setup() { // put your setup code here, to run once: // "comment with a double quote diff --git a/src/arduino.cc/builder/test/sketch8/SketchWithStruct.preprocessed.txt b/src/arduino.cc/builder/test/sketch8/SketchWithStruct.preprocessed.txt index cdfcd34e..35302e16 100644 --- a/src/arduino.cc/builder/test/sketch8/SketchWithStruct.preprocessed.txt +++ b/src/arduino.cc/builder/test/sketch8/SketchWithStruct.preprocessed.txt @@ -1,6 +1,6 @@ #include -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} /* START CODE */ struct A_NEW_TYPE { @@ -9,13 +9,13 @@ struct A_NEW_TYPE { int c; } foo; -#line 9 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 9 {{QuoteCppString .sketch.MainFile.Name}} void setup(); -#line 13 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 13 {{QuoteCppString .sketch.MainFile.Name}} void loop(); -#line 17 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 17 {{QuoteCppString .sketch.MainFile.Name}} void dostuff (A_NEW_TYPE * bar); -#line 9 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 9 {{QuoteCppString .sketch.MainFile.Name}} void setup() { } diff --git a/src/arduino.cc/builder/test/sketch_with_config/sketch_with_config.preprocessed.txt b/src/arduino.cc/builder/test/sketch_with_config/sketch_with_config.preprocessed.txt index 0216d1d0..2d56efd3 100644 --- a/src/arduino.cc/builder/test/sketch_with_config/sketch_with_config.preprocessed.txt +++ b/src/arduino.cc/builder/test/sketch_with_config/sketch_with_config.preprocessed.txt @@ -1,6 +1,6 @@ #include -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} #include "config.h" #ifdef DEBUG @@ -13,11 +13,11 @@ #include -#line 13 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 13 {{QuoteCppString .sketch.MainFile.Name}} void setup(); -#line 17 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 17 {{QuoteCppString .sketch.MainFile.Name}} void loop(); -#line 13 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 13 {{QuoteCppString .sketch.MainFile.Name}} void setup() { } diff --git a/src/arduino.cc/builder/test/sketch_with_ifdef/sketch.preprocessed.SAM.txt b/src/arduino.cc/builder/test/sketch_with_ifdef/sketch.preprocessed.SAM.txt index ae868905..2eb36912 100644 --- a/src/arduino.cc/builder/test/sketch_with_ifdef/sketch.preprocessed.SAM.txt +++ b/src/arduino.cc/builder/test/sketch_with_ifdef/sketch.preprocessed.SAM.txt @@ -1,18 +1,18 @@ #include -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} #if __SAM3X8E__ -#line 2 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 2 {{QuoteCppString .sketch.MainFile.Name}} void ifBranch(); -#line 9 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 9 {{QuoteCppString .sketch.MainFile.Name}} void f1(); -#line 10 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 10 {{QuoteCppString .sketch.MainFile.Name}} void f2(); -#line 12 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 12 {{QuoteCppString .sketch.MainFile.Name}} void setup(); -#line 14 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 14 {{QuoteCppString .sketch.MainFile.Name}} void loop(); -#line 2 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 2 {{QuoteCppString .sketch.MainFile.Name}} void ifBranch() { } #else diff --git a/src/arduino.cc/builder/test/sketch_with_ifdef/sketch.preprocessed.txt b/src/arduino.cc/builder/test/sketch_with_ifdef/sketch.preprocessed.txt index 88778cd2..31c86758 100644 --- a/src/arduino.cc/builder/test/sketch_with_ifdef/sketch.preprocessed.txt +++ b/src/arduino.cc/builder/test/sketch_with_ifdef/sketch.preprocessed.txt @@ -1,21 +1,21 @@ #include -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" -#line 1 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 1 {{QuoteCppString .sketch.MainFile.Name}} +#line 1 {{QuoteCppString .sketch.MainFile.Name}} #if __SAM3X8E__ void ifBranch() { } #else -#line 5 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 5 {{QuoteCppString .sketch.MainFile.Name}} void elseBranch(); -#line 9 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 9 {{QuoteCppString .sketch.MainFile.Name}} void f1(); -#line 10 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 10 {{QuoteCppString .sketch.MainFile.Name}} void f2(); -#line 12 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 12 {{QuoteCppString .sketch.MainFile.Name}} void setup(); -#line 14 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 14 {{QuoteCppString .sketch.MainFile.Name}} void loop(); -#line 5 "{{EscapeBackSlashes .sketch.MainFile.Name}}" +#line 5 {{QuoteCppString .sketch.MainFile.Name}} void elseBranch() { } #endif From e41896304d2a157c8102f570794785679e60b365 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 19 May 2016 15:51:25 +0200 Subject: [PATCH 3/5] Add utils.ParseCppString function function function function This allows properly parsing and unescaping a string literal. This function is not currently used (it was written to prepare for better parsing of #line directives, which might or might not be added later), but it might be relevant for other things in the future as well. Signed-off-by: Matthijs Kooijman --- src/arduino.cc/builder/test/utils_test.go | 28 +++++++++++++++ src/arduino.cc/builder/utils/utils.go | 44 +++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/arduino.cc/builder/test/utils_test.go b/src/arduino.cc/builder/test/utils_test.go index 3ff5610d..09cbce92 100644 --- a/src/arduino.cc/builder/test/utils_test.go +++ b/src/arduino.cc/builder/test/utils_test.go @@ -100,3 +100,31 @@ func TestQuoteCppString(t *testing.T) { require.Equal(t, expected, utils.QuoteCppString(input)) } } + +func TestParseCppString(t *testing.T) { + str, rest, ok := utils.ParseCppString(`foo`) + require.Equal(t, false, ok) + + str, rest, ok = utils.ParseCppString(`"foo`) + require.Equal(t, false, ok) + + str, rest, ok = utils.ParseCppString(`"foo"`) + require.Equal(t, true, ok) + require.Equal(t, `foo`, str) + require.Equal(t, ``, rest) + + str, rest, ok = utils.ParseCppString(`"foo\\bar"`) + require.Equal(t, true, ok) + require.Equal(t, `foo\bar`, str) + require.Equal(t, ``, rest) + + str, rest, ok = utils.ParseCppString(`"foo \"is\" quoted and \\\\bar\"\" escaped\\" and "then" some`) + require.Equal(t, true, ok) + require.Equal(t, `foo "is" quoted and \\bar"" escaped\`, str) + require.Equal(t, ` and "then" some`, rest) + + str, rest, ok = utils.ParseCppString(`" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~"`,) + require.Equal(t, true, ok) + require.Equal(t, ` !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_abcdefghijklmnopqrstuvwxyz{|}~`, str) + require.Equal(t, ``, rest) +} diff --git a/src/arduino.cc/builder/utils/utils.go b/src/arduino.cc/builder/utils/utils.go index 84dd854e..5bef8295 100644 --- a/src/arduino.cc/builder/utils/utils.go +++ b/src/arduino.cc/builder/utils/utils.go @@ -416,3 +416,47 @@ func QuoteCppString(str string) string { str = strings.Replace(str, "\"", "\\\"", -1) return "\"" + str + "\"" } + +// Parse a C-preprocessor string as emitted by the preprocessor. This +// is a string contained in double quotes, with any backslashes or +// quotes escaped with a backslash. If a valid string was present at the +// start of the given line, returns the unquoted string contents, the +// remaineder of the line (everything after the closing "), and true. +// Otherwise, returns the empty string, the entire line and false. +func ParseCppString(line string) (string, string, bool) { + // For details about how these strings are output by gcc, see: + // https://github.com/gcc-mirror/gcc/blob/a588355ab948cf551bc9d2b89f18e5ae5140f52c/libcpp/macro.c#L491-L511 + // Note that the documentaiton suggests all non-printable + // characters are also escaped, but the implementation does not + // actually do this. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51259 + if len(line) < 1 || line[0] != '"' { + return "", line, false + } + + i := 1 + res := "" + for { + if i >= len(line) { + return "", line, false + } + + switch (line[i]) { + // Backslash, next character is used unmodified + case '\\': + i++ + if i >= len(line) { + return "", line, false + } + res += string(line[i]) + break + // Quote, end of string + case '"': + return res, line[i+1:], true + default: + res += string(line[i]) + break + } + + i++ + } +} From 6740d85dddd5c21a4ac221a0a3b8ad34d681d311 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 19 May 2016 12:30:38 +0200 Subject: [PATCH 4/5] Remove unused ComparePrototypesFromSourceAndPreprocSource Signed-off-by: Matthijs Kooijman --- ...ototypes_from_source_and_preproc_source.go | 61 ------------------- src/arduino.cc/builder/types/context.go | 1 - 2 files changed, 62 deletions(-) delete mode 100644 src/arduino.cc/builder/compare_prototypes_from_source_and_preproc_source.go diff --git a/src/arduino.cc/builder/compare_prototypes_from_source_and_preproc_source.go b/src/arduino.cc/builder/compare_prototypes_from_source_and_preproc_source.go deleted file mode 100644 index 0f19b7f1..00000000 --- a/src/arduino.cc/builder/compare_prototypes_from_source_and_preproc_source.go +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 builder - -// XXX: Obsolete? - -import "arduino.cc/builder/types" - -type ComparePrototypesFromSourceAndPreprocSource struct{} - -func (s *ComparePrototypesFromSourceAndPreprocSource) Run(ctx *types.Context) error { - ctagsOfSource := ctx.CTagsOfSource - ctagsOfPreprocSource := ctx.CTagsOfPreprocessedSource - - actualCTags := []*types.CTag{} - for _, ctagOfPreprocSource := range ctagsOfPreprocSource { - if sliceContainsCTag(ctagsOfSource, ctagOfPreprocSource) { - actualCTags = append(actualCTags, ctagOfPreprocSource) - } - } - - ctx.CTagsCollected = actualCTags - - return nil -} - -func sliceContainsCTag(slice []*types.CTag, target *types.CTag) bool { - for _, value := range slice { - if value.FunctionName == target.FunctionName { - return true - } - } - return false -} diff --git a/src/arduino.cc/builder/types/context.go b/src/arduino.cc/builder/types/context.go index 09546536..1b04e1aa 100644 --- a/src/arduino.cc/builder/types/context.go +++ b/src/arduino.cc/builder/types/context.go @@ -65,7 +65,6 @@ type Context struct { // C++ Parsing CTagsOutput string CTagsTargetFile string - CTagsOfSource []*CTag CTagsOfPreprocessedSource []*CTag CTagsCollected []*CTag IncludeSection string From 81eadf324ef2f3d14034452bd40bd2f2eb75ff98 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Thu, 19 May 2016 16:20:53 +0200 Subject: [PATCH 5/5] Only pass sketch sources to ctags Previously, the .ino files from the sketch were concatenated, preprocessed and then the entire preprocessing result was passed through ctags. Since ctags isn't a perfect C/C++ parser, this would cause it to sometimes break on some complex constructs from library or system header files, preventing the entire sketch from compiling. This commit filters the result of preprocessing to only include lines that originate from the original sketch files and filter out any lines from included system or library header files. This filtering happens based on the line markers that the gcc preprocessor emits. Because of this, the CollectCTagsFromSketchFiles pass can be removed. This pass used to apply the same filtering, but on the tags generated by ctags. Since now only sketch lines are fed into ctags, the resulting ctags will obviously all originate from sketch files, so no need to further filter them. With this change, all sketches from the test suite still compile as expected. However, there is a fair chance that with this change, ctags will fail to parse some sketches. In general, to parse a C or C++ file, you need to keep track of the type environment and symbol table, since identifiers can either be variables or types, depending on the context. When lines from included files are cut off, this information is not available, and ctags will have to guess (though it might be doing that anyway, not sure if it normally does completely proper parsing). Signed-off-by: Matthijs Kooijman --- .../builder/container_add_prototypes.go | 2 +- .../builder/ctags/ctags_to_prototypes.go | 2 +- ...ketch_files.go => filter_sketch_source.go} | 68 +++++++++++++++---- .../builder/test/ctags_to_prototypes_test.go | 21 ------ src/arduino.cc/builder/types/context.go | 1 - 5 files changed, 55 insertions(+), 39 deletions(-) rename src/arduino.cc/builder/{collect_ctags_from_sketch_files.go => filter_sketch_source.go} (50%) diff --git a/src/arduino.cc/builder/container_add_prototypes.go b/src/arduino.cc/builder/container_add_prototypes.go index 28ba1645..8a138b44 100644 --- a/src/arduino.cc/builder/container_add_prototypes.go +++ b/src/arduino.cc/builder/container_add_prototypes.go @@ -42,10 +42,10 @@ func (s *ContainerAddPrototypes) Run(ctx *types.Context) error { commands := []types.Command{ &GCCPreprocRunner{TargetFileName: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E}, &ReadFileAndStoreInContext{Target: &ctx.SourceGccMinusE}, + &FilterSketchSource{Source: &ctx.SourceGccMinusE}, &CTagsTargetFileSaver{Source: &ctx.SourceGccMinusE, TargetFileName: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E}, &ctags.CTagsRunner{}, &ctags.CTagsParser{}, - &CollectCTagsFromSketchFiles{}, &ctags.CTagsToPrototypes{}, &PrototypesAdder{}, &SketchSaver{}, diff --git a/src/arduino.cc/builder/ctags/ctags_to_prototypes.go b/src/arduino.cc/builder/ctags/ctags_to_prototypes.go index c6528fec..954cb424 100644 --- a/src/arduino.cc/builder/ctags/ctags_to_prototypes.go +++ b/src/arduino.cc/builder/ctags/ctags_to_prototypes.go @@ -37,7 +37,7 @@ import ( type CTagsToPrototypes struct{} func (s *CTagsToPrototypes) Run(ctx *types.Context) error { - tags := ctx.CTagsCollected + tags := ctx.CTagsOfPreprocessedSource lineWhereToInsertPrototypes := findLineWhereToInsertPrototypes(tags) if lineWhereToInsertPrototypes != -1 { diff --git a/src/arduino.cc/builder/collect_ctags_from_sketch_files.go b/src/arduino.cc/builder/filter_sketch_source.go similarity index 50% rename from src/arduino.cc/builder/collect_ctags_from_sketch_files.go rename to src/arduino.cc/builder/filter_sketch_source.go index 979aba9f..34e5aa47 100644 --- a/src/arduino.cc/builder/collect_ctags_from_sketch_files.go +++ b/src/arduino.cc/builder/filter_sketch_source.go @@ -32,30 +32,68 @@ package builder import ( "arduino.cc/builder/types" "arduino.cc/builder/utils" + "strconv" + "strings" ) -type CollectCTagsFromSketchFiles struct{} +type FilterSketchSource struct { + Source *string +} -func (s *CollectCTagsFromSketchFiles) Run(ctx *types.Context) error { - sketchFileNames := collectSketchFileNamesFrom(ctx.Sketch) +func (s *FilterSketchSource) Run(ctx *types.Context) error { + lines := strings.Split(*s.Source, "\n") - allCtags := ctx.CTagsOfPreprocessedSource - ctagsOfSketch := []*types.CTag{} - for _, ctag := range allCtags { - if utils.SliceContains(sketchFileNames, ctag.Filename) { - ctagsOfSketch = append(ctagsOfSketch, ctag) - } + fileNames := []string{ctx.Sketch.MainFile.Name} + for _, file := range ctx.Sketch.OtherSketchFiles { + fileNames = append(fileNames, file.Name) } - ctx.CTagsCollected = ctagsOfSketch + inSketch := false + filtered := "" + + for _, line := range lines { + filename := parseLineMarker(line) + if filename != "" { + inSketch = utils.SliceContains(fileNames, filename) + } + + if inSketch { + filtered += line + "\n" + } + } + *s.Source = filtered return nil } -func collectSketchFileNamesFrom(sketch *types.Sketch) []string { - fileNames := []string{sketch.MainFile.Name} - for _, file := range sketch.OtherSketchFiles { - fileNames = append(fileNames, file.Name) +// Parses the given line as a gcc line marker and returns the contained +// filename. +func parseLineMarker(line string) string { + // A line marker contains the line number and filename and looks like: + // # 123 /path/to/file.cpp + // It can be followed by zero or more flag number that indicate the + // preprocessor state and can be ignored. + // For exact details on this format, see: + // https://github.com/gcc-mirror/gcc/blob/edd716b6b1caa1a5cb320a8cd7f626f30198e098/gcc/c-family/c-ppoutput.c#L413-L415 + + split := strings.SplitN(line, " ", 3) + if len(split) < 2 || split[0] != "#" { + return "" + } + + _, err := strconv.Atoi(split[1]) + if err != nil { + return "" } - return fileNames + + // If we get here, we found a # followed by a line number, so + // assume this is a line marker and see if the rest of the line + // starts with a string containing the filename + str, rest, ok := utils.ParseCppString(split[2]) + + if ok && (rest == "" || rest[0] == ' ') { + return str + } + return "" } + diff --git a/src/arduino.cc/builder/test/ctags_to_prototypes_test.go b/src/arduino.cc/builder/test/ctags_to_prototypes_test.go index ca1a2a6b..d379d97c 100644 --- a/src/arduino.cc/builder/test/ctags_to_prototypes_test.go +++ b/src/arduino.cc/builder/test/ctags_to_prototypes_test.go @@ -38,13 +38,6 @@ import ( "testing" ) -type CollectCtagsFromPreprocSource struct{} - -func (*CollectCtagsFromPreprocSource) Run(ctx *types.Context) error { - ctx.CTagsCollected = ctx.CTagsOfPreprocessedSource - return nil -} - func TestCTagsToPrototypesShouldListPrototypes(t *testing.T) { ctx := &types.Context{} @@ -55,7 +48,6 @@ func TestCTagsToPrototypesShouldListPrototypes(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -87,7 +79,6 @@ func TestCTagsToPrototypesShouldListTemplates(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -117,7 +108,6 @@ func TestCTagsToPrototypesShouldListTemplates2(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -148,7 +138,6 @@ func TestCTagsToPrototypesShouldDealWithClasses(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -174,7 +163,6 @@ func TestCTagsToPrototypesShouldDealWithStructs(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -204,7 +192,6 @@ func TestCTagsToPrototypesShouldDealWithMacros(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -236,7 +223,6 @@ func TestCTagsToPrototypesShouldDealFunctionWithDifferentSignatures(t *testing.T commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -264,7 +250,6 @@ func TestCTagsToPrototypesClassMembersAreFilteredOut(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -293,7 +278,6 @@ func TestCTagsToPrototypesStructWithFunctions(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -322,7 +306,6 @@ func TestCTagsToPrototypesDefaultArguments(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -352,7 +335,6 @@ func TestCTagsToPrototypesNamespace(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -381,7 +363,6 @@ func TestCTagsToPrototypesStatic(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -412,7 +393,6 @@ func TestCTagsToPrototypesFunctionPointer(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } @@ -442,7 +422,6 @@ func TestCTagsToPrototypesFunctionPointers(t *testing.T) { commands := []types.Command{ &ctags.CTagsParser{}, - &CollectCtagsFromPreprocSource{}, &ctags.CTagsToPrototypes{}, } diff --git a/src/arduino.cc/builder/types/context.go b/src/arduino.cc/builder/types/context.go index 1b04e1aa..8be5a858 100644 --- a/src/arduino.cc/builder/types/context.go +++ b/src/arduino.cc/builder/types/context.go @@ -66,7 +66,6 @@ type Context struct { CTagsOutput string CTagsTargetFile string CTagsOfPreprocessedSource []*CTag - CTagsCollected []*CTag IncludeSection string LineOffset int PrototypesSection string