Skip to content

Commit 81eadf3

Browse files
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 <[email protected]>
1 parent 6740d85 commit 81eadf3

File tree

5 files changed

+55
-39
lines changed

5 files changed

+55
-39
lines changed

src/arduino.cc/builder/container_add_prototypes.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ func (s *ContainerAddPrototypes) Run(ctx *types.Context) error {
4242
commands := []types.Command{
4343
&GCCPreprocRunner{TargetFileName: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E},
4444
&ReadFileAndStoreInContext{Target: &ctx.SourceGccMinusE},
45+
&FilterSketchSource{Source: &ctx.SourceGccMinusE},
4546
&CTagsTargetFileSaver{Source: &ctx.SourceGccMinusE, TargetFileName: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E},
4647
&ctags.CTagsRunner{},
4748
&ctags.CTagsParser{},
48-
&CollectCTagsFromSketchFiles{},
4949
&ctags.CTagsToPrototypes{},
5050
&PrototypesAdder{},
5151
&SketchSaver{},

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import (
3737
type CTagsToPrototypes struct{}
3838

3939
func (s *CTagsToPrototypes) Run(ctx *types.Context) error {
40-
tags := ctx.CTagsCollected
40+
tags := ctx.CTagsOfPreprocessedSource
4141

4242
lineWhereToInsertPrototypes := findLineWhereToInsertPrototypes(tags)
4343
if lineWhereToInsertPrototypes != -1 {

src/arduino.cc/builder/collect_ctags_from_sketch_files.go renamed to src/arduino.cc/builder/filter_sketch_source.go

+53-15
Original file line numberDiff line numberDiff line change
@@ -32,30 +32,68 @@ package builder
3232
import (
3333
"arduino.cc/builder/types"
3434
"arduino.cc/builder/utils"
35+
"strconv"
36+
"strings"
3537
)
3638

37-
type CollectCTagsFromSketchFiles struct{}
39+
type FilterSketchSource struct {
40+
Source *string
41+
}
3842

39-
func (s *CollectCTagsFromSketchFiles) Run(ctx *types.Context) error {
40-
sketchFileNames := collectSketchFileNamesFrom(ctx.Sketch)
43+
func (s *FilterSketchSource) Run(ctx *types.Context) error {
44+
lines := strings.Split(*s.Source, "\n")
4145

42-
allCtags := ctx.CTagsOfPreprocessedSource
43-
ctagsOfSketch := []*types.CTag{}
44-
for _, ctag := range allCtags {
45-
if utils.SliceContains(sketchFileNames, ctag.Filename) {
46-
ctagsOfSketch = append(ctagsOfSketch, ctag)
47-
}
46+
fileNames := []string{ctx.Sketch.MainFile.Name}
47+
for _, file := range ctx.Sketch.OtherSketchFiles {
48+
fileNames = append(fileNames, file.Name)
4849
}
4950

50-
ctx.CTagsCollected = ctagsOfSketch
51+
inSketch := false
52+
filtered := ""
53+
54+
for _, line := range lines {
55+
filename := parseLineMarker(line)
56+
if filename != "" {
57+
inSketch = utils.SliceContains(fileNames, filename)
58+
}
59+
60+
if inSketch {
61+
filtered += line + "\n"
62+
}
63+
}
5164

65+
*s.Source = filtered
5266
return nil
5367
}
5468

55-
func collectSketchFileNamesFrom(sketch *types.Sketch) []string {
56-
fileNames := []string{sketch.MainFile.Name}
57-
for _, file := range sketch.OtherSketchFiles {
58-
fileNames = append(fileNames, file.Name)
69+
// Parses the given line as a gcc line marker and returns the contained
70+
// filename.
71+
func parseLineMarker(line string) string {
72+
// A line marker contains the line number and filename and looks like:
73+
// # 123 /path/to/file.cpp
74+
// It can be followed by zero or more flag number that indicate the
75+
// preprocessor state and can be ignored.
76+
// For exact details on this format, see:
77+
// https://github.com/gcc-mirror/gcc/blob/edd716b6b1caa1a5cb320a8cd7f626f30198e098/gcc/c-family/c-ppoutput.c#L413-L415
78+
79+
split := strings.SplitN(line, " ", 3)
80+
if len(split) < 2 || split[0] != "#" {
81+
return ""
82+
}
83+
84+
_, err := strconv.Atoi(split[1])
85+
if err != nil {
86+
return ""
5987
}
60-
return fileNames
88+
89+
// If we get here, we found a # followed by a line number, so
90+
// assume this is a line marker and see if the rest of the line
91+
// starts with a string containing the filename
92+
str, rest, ok := utils.ParseCppString(split[2])
93+
94+
if ok && (rest == "" || rest[0] == ' ') {
95+
return str
96+
}
97+
return ""
6198
}
99+

src/arduino.cc/builder/test/ctags_to_prototypes_test.go

-21
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,6 @@ import (
3838
"testing"
3939
)
4040

41-
type CollectCtagsFromPreprocSource struct{}
42-
43-
func (*CollectCtagsFromPreprocSource) Run(ctx *types.Context) error {
44-
ctx.CTagsCollected = ctx.CTagsOfPreprocessedSource
45-
return nil
46-
}
47-
4841
func TestCTagsToPrototypesShouldListPrototypes(t *testing.T) {
4942
ctx := &types.Context{}
5043

@@ -55,7 +48,6 @@ func TestCTagsToPrototypesShouldListPrototypes(t *testing.T) {
5548

5649
commands := []types.Command{
5750
&ctags.CTagsParser{},
58-
&CollectCtagsFromPreprocSource{},
5951
&ctags.CTagsToPrototypes{},
6052
}
6153

@@ -87,7 +79,6 @@ func TestCTagsToPrototypesShouldListTemplates(t *testing.T) {
8779

8880
commands := []types.Command{
8981
&ctags.CTagsParser{},
90-
&CollectCtagsFromPreprocSource{},
9182
&ctags.CTagsToPrototypes{},
9283
}
9384

@@ -117,7 +108,6 @@ func TestCTagsToPrototypesShouldListTemplates2(t *testing.T) {
117108

118109
commands := []types.Command{
119110
&ctags.CTagsParser{},
120-
&CollectCtagsFromPreprocSource{},
121111
&ctags.CTagsToPrototypes{},
122112
}
123113

@@ -148,7 +138,6 @@ func TestCTagsToPrototypesShouldDealWithClasses(t *testing.T) {
148138

149139
commands := []types.Command{
150140
&ctags.CTagsParser{},
151-
&CollectCtagsFromPreprocSource{},
152141
&ctags.CTagsToPrototypes{},
153142
}
154143

@@ -174,7 +163,6 @@ func TestCTagsToPrototypesShouldDealWithStructs(t *testing.T) {
174163

175164
commands := []types.Command{
176165
&ctags.CTagsParser{},
177-
&CollectCtagsFromPreprocSource{},
178166
&ctags.CTagsToPrototypes{},
179167
}
180168

@@ -204,7 +192,6 @@ func TestCTagsToPrototypesShouldDealWithMacros(t *testing.T) {
204192

205193
commands := []types.Command{
206194
&ctags.CTagsParser{},
207-
&CollectCtagsFromPreprocSource{},
208195
&ctags.CTagsToPrototypes{},
209196
}
210197

@@ -236,7 +223,6 @@ func TestCTagsToPrototypesShouldDealFunctionWithDifferentSignatures(t *testing.T
236223

237224
commands := []types.Command{
238225
&ctags.CTagsParser{},
239-
&CollectCtagsFromPreprocSource{},
240226
&ctags.CTagsToPrototypes{},
241227
}
242228

@@ -264,7 +250,6 @@ func TestCTagsToPrototypesClassMembersAreFilteredOut(t *testing.T) {
264250

265251
commands := []types.Command{
266252
&ctags.CTagsParser{},
267-
&CollectCtagsFromPreprocSource{},
268253
&ctags.CTagsToPrototypes{},
269254
}
270255

@@ -293,7 +278,6 @@ func TestCTagsToPrototypesStructWithFunctions(t *testing.T) {
293278

294279
commands := []types.Command{
295280
&ctags.CTagsParser{},
296-
&CollectCtagsFromPreprocSource{},
297281
&ctags.CTagsToPrototypes{},
298282
}
299283

@@ -322,7 +306,6 @@ func TestCTagsToPrototypesDefaultArguments(t *testing.T) {
322306

323307
commands := []types.Command{
324308
&ctags.CTagsParser{},
325-
&CollectCtagsFromPreprocSource{},
326309
&ctags.CTagsToPrototypes{},
327310
}
328311

@@ -352,7 +335,6 @@ func TestCTagsToPrototypesNamespace(t *testing.T) {
352335

353336
commands := []types.Command{
354337
&ctags.CTagsParser{},
355-
&CollectCtagsFromPreprocSource{},
356338
&ctags.CTagsToPrototypes{},
357339
}
358340

@@ -381,7 +363,6 @@ func TestCTagsToPrototypesStatic(t *testing.T) {
381363

382364
commands := []types.Command{
383365
&ctags.CTagsParser{},
384-
&CollectCtagsFromPreprocSource{},
385366
&ctags.CTagsToPrototypes{},
386367
}
387368

@@ -412,7 +393,6 @@ func TestCTagsToPrototypesFunctionPointer(t *testing.T) {
412393

413394
commands := []types.Command{
414395
&ctags.CTagsParser{},
415-
&CollectCtagsFromPreprocSource{},
416396
&ctags.CTagsToPrototypes{},
417397
}
418398

@@ -442,7 +422,6 @@ func TestCTagsToPrototypesFunctionPointers(t *testing.T) {
442422

443423
commands := []types.Command{
444424
&ctags.CTagsParser{},
445-
&CollectCtagsFromPreprocSource{},
446425
&ctags.CTagsToPrototypes{},
447426
}
448427

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

-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ type Context struct {
6666
CTagsOutput string
6767
CTagsTargetFile string
6868
CTagsOfPreprocessedSource []*CTag
69-
CTagsCollected []*CTag
7069
IncludeSection string
7170
LineOffset int
7271
PrototypesSection string

0 commit comments

Comments
 (0)