Skip to content

Commit 40328a0

Browse files
author
Federico Fissore
committed
Reviewed how prototypes are generated and ctags output is parsed:
- added --line-directives to ctags command line: makes "line" fields aware of #line directives (thanks @matthijskooijman for the hint) - prototypes get generated after ctags are produced for both gcc -E output and original sketch. This allows to consider functions used in callbacks. See #50 - each prototype is preceded by #line directives that points to its function. This allows error in function declarations themselves to be properly reported to the IDE - split ctags target files (added "ctags_target_for_gcc_minus_e.cpp") for easier debugging Signed-off-by: Federico Fissore <[email protected]>
1 parent 1c13cc8 commit 40328a0

27 files changed

+1027
-297
lines changed

src/arduino.cc/builder/compare_prototypes_from_source_and_preproc_source.go

+7-8
Original file line numberDiff line numberDiff line change
@@ -31,24 +31,23 @@ package builder
3131

3232
import (
3333
"arduino.cc/builder/constants"
34-
"arduino.cc/builder/types"
3534
"arduino.cc/builder/utils"
3635
)
3736

3837
type ComparePrototypesFromSourceAndPreprocSource struct{}
3938

4039
func (s *ComparePrototypesFromSourceAndPreprocSource) Run(context map[string]interface{}) error {
41-
prototypesOfSource := context[constants.CTX_PROTOTYPES_OF_SOURCE].([]*types.Prototype)
42-
prototypesOfPreprocSource := context[constants.CTX_PROTOTYPES_OF_PREPROC_SOURCE].([]*types.Prototype)
40+
ctagsOfSource := context[constants.CTX_CTAGS_OF_SOURCE].([]map[string]string)
41+
ctagsOfPreprocSource := context[constants.CTX_CTAGS_OF_PREPROC_SOURCE].([]map[string]string)
4342

44-
actualPrototypes := []*types.Prototype{}
45-
for _, prototypeOfPreprocSource := range prototypesOfPreprocSource {
46-
if utils.SliceContainsPrototype(prototypesOfSource, prototypeOfPreprocSource) {
47-
actualPrototypes = append(actualPrototypes, prototypeOfPreprocSource)
43+
actualCTags := []map[string]string{}
44+
for _, ctagOfPreprocSource := range ctagsOfPreprocSource {
45+
if utils.SliceContainsCTag(ctagsOfSource, ctagOfPreprocSource) {
46+
actualCTags = append(actualCTags, ctagOfPreprocSource)
4847
}
4948
}
5049

51-
context[constants.CTX_PROTOTYPES] = actualPrototypes
50+
context[constants.CTX_COLLECTED_CTAGS] = actualCTags
5251

5352
return nil
5453
}

src/arduino.cc/builder/constants/constants.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const BUILD_PROPERTIES_TOOLS_KEY = "tools"
7272
const BUILD_PROPERTIES_VID = "vid"
7373
const COAN = "coan"
7474
const CTAGS = "ctags"
75+
const CTAGS_FIELD_FUNCTION_NAME = "functionName"
7576
const CTX_ACTUAL_PLATFORM = "actualPlatform"
7677
const CTX_ARCHIVE_FILE_PATH_CORE = "archiveFileCore"
7778
const CTX_BUILD_CORE = "buildCore"
@@ -82,13 +83,15 @@ const CTX_BUILD_PATH = "buildPath"
8283
const CTX_BUILD_PROPERTIES = "buildProperties"
8384
const CTX_BUILD_PROPERTIES_RUNTIME_IDE_VERSION = "runtime.ide.version"
8485
const CTX_BUILT_IN_LIBRARIES_FOLDERS = "builtInLibrariesFolders"
86+
const CTX_COLLECTED_CTAGS = "collectedCTags"
8587
const CTX_COLLECTED_SOURCE_FILES_QUEUE = "collectedSourceFilesQueue"
8688
const CTX_CORE_BUILD_PATH = "coreBuildPath"
89+
const CTX_CTAGS_OF_PREPROC_SOURCE = "ctagsOfPreprocSource"
90+
const CTX_CTAGS_OF_SOURCE = "ctagsOfSource"
8791
const CTX_CTAGS_OUTPUT = "ctagsOutput"
8892
const CTX_CTAGS_TEMP_FILE_NAME = "ctagsTempFileName"
8993
const CTX_CUSTOM_BUILD_PROPERTIES = "customBuildProperties"
9094
const CTX_DEBUG_LEVEL = "debugLevel"
91-
const CTX_LINE_WHERE_TO_INSERT_PROTOTYPES = "lineWhereToInsertPrototypes"
9295
const CTX_FOLDERS_WITH_SOURCES_QUEUE = "foldersWithSourcesQueue"
9396
const CTX_FQBN = "fqbn"
9497
const CTX_GCC_MINUS_E_SOURCE = "gccMinusESource"
@@ -106,15 +109,16 @@ const CTX_LIBRARIES = "libraries"
106109
const CTX_LIBRARY_DISCOVERY_RECURSION_DEPTH = "libraryDiscoveryRecursionDepth"
107110
const CTX_LIBRARY_RESOLUTION_RESULTS = "libraryResolutionResults"
108111
const CTX_LINE_OFFSET = "lineOffset"
112+
const CTX_LINE_WHERE_TO_INSERT_PROTOTYPES = "lineWhereToInsertPrototypes"
113+
const CTX_LINE_WHERE_TO_INSERT_PROTOTYPES_OF_PREPROC_SOURCE = "lineWhereToInsertPrototypesOfPreprocSource"
114+
const CTX_LINE_WHERE_TO_INSERT_PROTOTYPES_OF_SOURCE = "lineWhereToInsertPrototypesOfSource"
109115
const CTX_LOGGER = "logger"
110116
const CTX_OBJECT_FILES_LIBRARIES = "objectFilesLibraries"
111117
const CTX_OBJECT_FILES_SKETCH = "objectFilesSketch"
112118
const CTX_OTHER_LIBRARIES_FOLDERS = "otherLibrariesFolders"
113119
const CTX_PLATFORM_KEYS_REWRITE = "platformKeysRewrite"
114120
const CTX_PREPROC_PATH = "preprocPath"
115121
const CTX_PROTOTYPE_SECTION = "prototypeSection"
116-
const CTX_PROTOTYPES_OF_PREPROC_SOURCE = "prototypesOfPreprocSource"
117-
const CTX_PROTOTYPES_OF_SOURCE = "prototypesOfSource"
118122
const CTX_PROTOTYPES = "prototypes"
119123
const CTX_SKETCH_BUILD_PATH = "sketchBuildPath"
120124
const CTX_SKETCH_LOCATION = "sketchLocation"
@@ -134,6 +138,7 @@ const FILE_BOARDS_TXT = "boards.txt"
134138
const FILE_BUILTIN_TOOLS_VERSIONS_TXT = "builtin_tools_versions.txt"
135139
const FILE_COAN_TARGET = "coan_target.cpp"
136140
const FILE_CTAGS_TARGET = "ctags_target.cpp"
141+
const FILE_CTAGS_TARGET_FOR_GCC_MINUS_E = "ctags_target_for_gcc_minus_e.cpp"
137142
const FILE_GCC_PREPROC_TARGET = "gcc_preproc_target.cpp"
138143
const FILE_PLATFORM_KEYS_REWRITE_TXT = "platform.keys.rewrite.txt"
139144
const FILE_PLATFORM_LOCAL_TXT = "platform.local.txt"

src/arduino.cc/builder/container_add_prototypes.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,14 @@ type ContainerAddPrototypes struct{}
4040
func (s *ContainerAddPrototypes) Run(context map[string]interface{}) error {
4141
commands := []types.Command{
4242
&GCCPreprocRunner{},
43-
&CTagsTargetFileSaver{SourceField: constants.CTX_GCC_MINUS_E_SOURCE},
43+
&CTagsTargetFileSaver{SourceField: constants.CTX_GCC_MINUS_E_SOURCE, Filename: constants.FILE_CTAGS_TARGET_FOR_GCC_MINUS_E},
4444
&CTagsRunner{},
45-
&CTagsParser{PrototypesField: constants.CTX_PROTOTYPES_OF_PREPROC_SOURCE},
46-
&CTagsTargetFileSaver{SourceField: constants.CTX_SOURCE},
45+
&CTagsParser{CTagsField: constants.CTX_CTAGS_OF_PREPROC_SOURCE},
46+
&CTagsTargetFileSaver{SourceField: constants.CTX_SOURCE, Filename: constants.FILE_CTAGS_TARGET},
4747
&CTagsRunner{},
48-
&CTagsParser{PrototypesField: constants.CTX_PROTOTYPES_OF_SOURCE},
48+
&CTagsParser{CTagsField: constants.CTX_CTAGS_OF_SOURCE},
4949
&ComparePrototypesFromSourceAndPreprocSource{},
50+
&CTagsToPrototypes{},
5051
&PrototypesAdder{},
5152
&SketchSaver{},
5253
}

src/arduino.cc/builder/ctags_parser.go

+4-88
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ package builder
3131

3232
import (
3333
"arduino.cc/builder/constants"
34-
"arduino.cc/builder/types"
35-
"arduino.cc/builder/utils"
3634
"strconv"
3735
"strings"
3836
)
@@ -42,7 +40,6 @@ const FIELD_LINE = "line"
4240
const FIELD_SIGNATURE = "signature"
4341
const FIELD_RETURNTYPE = "returntype"
4442
const FIELD_CODE = "code"
45-
const FIELD_FUNCTION_NAME = "functionName"
4643
const FIELD_CLASS = "class"
4744
const FIELD_STRUCT = "struct"
4845
const FIELD_NAMESPACE = "namespace"
@@ -61,7 +58,7 @@ var KNOWN_TAG_KINDS = map[string]bool{"prototype": true, "function": true}
6158
var FIELDS_MARKING_UNHANDLED_TAGS = []string{FIELD_CLASS, FIELD_STRUCT, FIELD_NAMESPACE}
6259

6360
type CTagsParser struct {
64-
PrototypesField string
61+
CTagsField string
6562
}
6663

6764
func (s *CTagsParser) Run(context map[string]interface{}) error {
@@ -82,92 +79,11 @@ func (s *CTagsParser) Run(context map[string]interface{}) error {
8279
removeDuplicate(tags)
8380
skipTagsWhere(tags, prototypeAndCodeDontMatch)
8481

85-
lineWhereToInsertPrototypes, err := findLineWhereToInsertPrototypes(tags)
86-
if err != nil {
87-
return utils.WrapError(err)
88-
}
89-
if lineWhereToInsertPrototypes != -1 {
90-
context[constants.CTX_LINE_WHERE_TO_INSERT_PROTOTYPES] = lineWhereToInsertPrototypes
91-
}
92-
93-
prototypes := toPrototypes(tags)
94-
95-
context[s.PrototypesField] = prototypes
82+
context[s.CTagsField] = tags
9683

9784
return nil
9885
}
9986

100-
func findLineWhereToInsertPrototypes(tags []map[string]string) (int, error) {
101-
firstFunctionLine, err := firstFunctionAtLine(tags)
102-
if err != nil {
103-
return -1, utils.WrapError(err)
104-
}
105-
firstFunctionPointerAsArgument, err := firstFunctionPointerUsedAsArgument(tags)
106-
if err != nil {
107-
return -1, utils.WrapError(err)
108-
}
109-
if firstFunctionLine != -1 && firstFunctionPointerAsArgument != -1 {
110-
if firstFunctionLine < firstFunctionPointerAsArgument {
111-
return firstFunctionLine, nil
112-
} else {
113-
return firstFunctionPointerAsArgument, nil
114-
}
115-
} else if firstFunctionLine == -1 {
116-
return firstFunctionPointerAsArgument, nil
117-
} else {
118-
return firstFunctionLine, nil
119-
}
120-
}
121-
122-
func firstFunctionPointerUsedAsArgument(tags []map[string]string) (int, error) {
123-
functionNames := collectFunctionNames(tags)
124-
for _, tag := range tags {
125-
if functionNameUsedAsFunctionPointerIn(tag, functionNames) {
126-
return strconv.Atoi(tag[FIELD_LINE])
127-
}
128-
}
129-
return -1, nil
130-
}
131-
132-
func functionNameUsedAsFunctionPointerIn(tag map[string]string, functionNames []string) bool {
133-
for _, functionName := range functionNames {
134-
if strings.Index(tag[FIELD_CODE], "&"+functionName) != -1 {
135-
return true
136-
}
137-
}
138-
return false
139-
}
140-
141-
func collectFunctionNames(tags []map[string]string) []string {
142-
names := []string{}
143-
for _, tag := range tags {
144-
if tag[FIELD_KIND] == KIND_FUNCTION {
145-
names = append(names, tag[FIELD_FUNCTION_NAME])
146-
}
147-
}
148-
return names
149-
}
150-
151-
func firstFunctionAtLine(tags []map[string]string) (int, error) {
152-
for _, tag := range tags {
153-
if !tagIsUnknown(tag) && !tagHasAtLeastOneField(tag, FIELDS_MARKING_UNHANDLED_TAGS) && tag[FIELD_KIND] == KIND_FUNCTION {
154-
return strconv.Atoi(tag[FIELD_LINE])
155-
}
156-
}
157-
return -1, nil
158-
}
159-
160-
func toPrototypes(tags []map[string]string) []*types.Prototype {
161-
prototypes := []*types.Prototype{}
162-
for _, tag := range tags {
163-
if tag[FIELD_SKIP] != TRUE {
164-
ctag := types.Prototype{FunctionName: tag[FIELD_FUNCTION_NAME], Prototype: tag[KIND_PROTOTYPE], Modifiers: tag[KIND_PROTOTYPE_MODIFIERS], Fields: tag}
165-
prototypes = append(prototypes, &ctag)
166-
}
167-
}
168-
return prototypes
169-
}
170-
17187
func addPrototypes(tags []map[string]string) {
17288
for _, tag := range tags {
17389
if tag[FIELD_SKIP] != TRUE {
@@ -188,7 +104,7 @@ func addPrototype(tag map[string]string) {
188104
return
189105
}
190106

191-
tag[KIND_PROTOTYPE] = tag[FIELD_RETURNTYPE] + " " + tag[FIELD_FUNCTION_NAME] + tag[FIELD_SIGNATURE] + ";"
107+
tag[KIND_PROTOTYPE] = tag[FIELD_RETURNTYPE] + " " + tag[constants.CTAGS_FIELD_FUNCTION_NAME] + tag[FIELD_SIGNATURE] + ";"
192108

193109
tag[KIND_PROTOTYPE_MODIFIERS] = ""
194110
if strings.Index(tag[FIELD_CODE], STATIC+" ") != -1 {
@@ -289,7 +205,7 @@ func parseTag(row string) map[string]string {
289205
tag := make(map[string]string)
290206
parts := strings.Split(row, "\t")
291207

292-
tag[FIELD_FUNCTION_NAME] = parts[0]
208+
tag[constants.CTAGS_FIELD_FUNCTION_NAME] = parts[0]
293209
parts = parts[1:]
294210

295211
for _, part := range parts {

src/arduino.cc/builder/ctags_target_file_saver.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ import (
3939

4040
type CTagsTargetFileSaver struct {
4141
SourceField string
42+
Filename string
4243
}
4344

4445
func (s *CTagsTargetFileSaver) Run(context map[string]interface{}) error {
@@ -50,7 +51,7 @@ func (s *CTagsTargetFileSaver) Run(context map[string]interface{}) error {
5051
return utils.WrapError(err)
5152
}
5253

53-
ctagsTargetFileName := filepath.Join(preprocPath, constants.FILE_CTAGS_TARGET)
54+
ctagsTargetFileName := filepath.Join(preprocPath, s.Filename)
5455
err = ioutil.WriteFile(ctagsTargetFileName, []byte(source), os.FileMode(0644))
5556
if err != nil {
5657
return utils.WrapError(err)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package builder
2+
3+
import (
4+
"arduino.cc/builder/constants"
5+
"arduino.cc/builder/types"
6+
"arduino.cc/builder/utils"
7+
"strconv"
8+
"strings"
9+
)
10+
11+
type CTagsToPrototypes struct{}
12+
13+
func (s *CTagsToPrototypes) Run(context map[string]interface{}) error {
14+
tags := context[constants.CTX_COLLECTED_CTAGS].([]map[string]string)
15+
16+
lineWhereToInsertPrototypes, err := findLineWhereToInsertPrototypes(tags)
17+
if err != nil {
18+
return utils.WrapError(err)
19+
}
20+
if lineWhereToInsertPrototypes != -1 {
21+
context[constants.CTX_LINE_WHERE_TO_INSERT_PROTOTYPES] = lineWhereToInsertPrototypes
22+
}
23+
24+
prototypes := toPrototypes(tags)
25+
context[constants.CTX_PROTOTYPES] = prototypes
26+
27+
return nil
28+
}
29+
30+
func findLineWhereToInsertPrototypes(tags []map[string]string) (int, error) {
31+
firstFunctionLine, err := firstFunctionAtLine(tags)
32+
if err != nil {
33+
return -1, utils.WrapError(err)
34+
}
35+
firstFunctionPointerAsArgument, err := firstFunctionPointerUsedAsArgument(tags)
36+
if err != nil {
37+
return -1, utils.WrapError(err)
38+
}
39+
if firstFunctionLine != -1 && firstFunctionPointerAsArgument != -1 {
40+
if firstFunctionLine < firstFunctionPointerAsArgument {
41+
return firstFunctionLine, nil
42+
} else {
43+
return firstFunctionPointerAsArgument, nil
44+
}
45+
} else if firstFunctionLine == -1 {
46+
return firstFunctionPointerAsArgument, nil
47+
} else {
48+
return firstFunctionLine, nil
49+
}
50+
}
51+
52+
func firstFunctionPointerUsedAsArgument(tags []map[string]string) (int, error) {
53+
functionNames := collectFunctionNames(tags)
54+
for _, tag := range tags {
55+
if functionNameUsedAsFunctionPointerIn(tag, functionNames) {
56+
return strconv.Atoi(tag[FIELD_LINE])
57+
}
58+
}
59+
return -1, nil
60+
}
61+
62+
func functionNameUsedAsFunctionPointerIn(tag map[string]string, functionNames []string) bool {
63+
for _, functionName := range functionNames {
64+
if strings.Index(tag[FIELD_CODE], "&"+functionName) != -1 {
65+
return true
66+
}
67+
}
68+
return false
69+
}
70+
71+
func collectFunctionNames(tags []map[string]string) []string {
72+
names := []string{}
73+
for _, tag := range tags {
74+
if tag[FIELD_KIND] == KIND_FUNCTION {
75+
names = append(names, tag[constants.CTAGS_FIELD_FUNCTION_NAME])
76+
}
77+
}
78+
return names
79+
}
80+
81+
func firstFunctionAtLine(tags []map[string]string) (int, error) {
82+
for _, tag := range tags {
83+
if !tagIsUnknown(tag) && !tagHasAtLeastOneField(tag, FIELDS_MARKING_UNHANDLED_TAGS) && tag[FIELD_KIND] == KIND_FUNCTION {
84+
return strconv.Atoi(tag[FIELD_LINE])
85+
}
86+
}
87+
return -1, nil
88+
}
89+
90+
func toPrototypes(tags []map[string]string) []*types.Prototype {
91+
prototypes := []*types.Prototype{}
92+
for _, tag := range tags {
93+
if tag[FIELD_SKIP] != TRUE {
94+
ctag := types.Prototype{FunctionName: tag[constants.CTAGS_FIELD_FUNCTION_NAME], Prototype: tag[KIND_PROTOTYPE], Modifiers: tag[KIND_PROTOTYPE_MODIFIERS], Line: tag[FIELD_LINE], Fields: tag}
95+
prototypes = append(prototypes, &ctag)
96+
}
97+
}
98+
return prototypes
99+
}

src/arduino.cc/builder/hardware/platform.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# ------------------------------
33
tools.ctags.path={runtime.tools.ctags.path}
44
tools.ctags.cmd.path={path}/ctags
5-
tools.ctags.pattern="{cmd.path}" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns "{source_file}"
5+
tools.ctags.pattern="{cmd.path}" -u --language-force=c++ -f - --c++-kinds=svpf --fields=KSTtzns --line-directives "{source_file}"
66

77
# additional entries
88
tools.avrdude.path={runtime.tools.avrdude.path}

src/arduino.cc/builder/prototypes_adder.go

+2-4
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,7 @@ func (s *PrototypesAdder) Run(context map[string]interface{}) error {
5252
return nil
5353
}
5454

55-
firstFunctionChar := len(strings.Join(sourceRows[:firstFunctionLine-1], "\n")) + 1
56-
if firstFunctionLine > 1 {
57-
firstFunctionLine -= context[constants.CTX_LINE_OFFSET].(int)
58-
}
55+
firstFunctionChar := len(strings.Join(sourceRows[:firstFunctionLine+context[constants.CTX_LINE_OFFSET].(int)-1], "\n")) + 1
5956
prototypeSection := composePrototypeSection(firstFunctionLine, context[constants.CTX_PROTOTYPES].([]*types.Prototype))
6057
context[constants.CTX_PROTOTYPE_SECTION] = prototypeSection
6158
source = source[:firstFunctionChar] + prototypeSection + source[firstFunctionChar:]
@@ -85,6 +82,7 @@ func composePrototypeSection(line int, prototypes []*types.Prototype) string {
8582
func joinPrototypes(prototypes []*types.Prototype) string {
8683
prototypesSlice := []string{}
8784
for _, proto := range prototypes {
85+
prototypesSlice = append(prototypesSlice, "#line "+proto.Line)
8886
prototypeParts := []string{}
8987
if proto.Modifiers != "" {
9088
prototypeParts = append(prototypeParts, proto.Modifiers)

0 commit comments

Comments
 (0)