Skip to content

Commit 9199f50

Browse files
author
Federico Fissore
committedNov 9, 2015
If code line contains a function pointer (eg: &functionName), then prototypes
are written above that line. Mitigates #50 Signed-off-by: Federico Fissore <[email protected]>
1 parent 45b5865 commit 9199f50

File tree

7 files changed

+184
-50
lines changed

7 files changed

+184
-50
lines changed
 

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ const CTX_CTAGS_OUTPUT = "ctagsOutput"
8888
const CTX_CTAGS_TEMP_FILE_NAME = "ctagsTempFileName"
8989
const CTX_CUSTOM_BUILD_PROPERTIES = "customBuildProperties"
9090
const CTX_DEBUG_LEVEL = "debugLevel"
91-
const CTX_FIRST_FUNCTION_AT_LINE = "firstFunctionAtLine"
91+
const CTX_LINE_WHERE_TO_INSERT_PROTOTYPES = "lineWhereToInsertPrototypes"
9292
const CTX_FOLDERS_WITH_SOURCES_QUEUE = "foldersWithSourcesQueue"
9393
const CTX_FQBN = "fqbn"
9494
const CTX_GCC_MINUS_E_SOURCE = "gccMinusESource"

‎src/arduino.cc/builder/ctags_parser.go

+108-47
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,16 @@ const FIELD_NAMESPACE = "namespace"
4949
const FIELD_SKIP = "skipMe"
5050

5151
const KIND_PROTOTYPE = "prototype"
52+
const KIND_FUNCTION = "function"
5253
const KIND_PROTOTYPE_MODIFIERS = "prototype_modifiers"
5354

5455
const TEMPLATE = "template"
5556
const STATIC = "static"
57+
const TRUE = "true"
5658

5759
var FIELDS = map[string]bool{"kind": true, "line": true, "typeref": true, "signature": true, "returntype": true, "class": true, "struct": true, "namespace": true}
5860
var KNOWN_TAG_KINDS = map[string]bool{"prototype": true, "function": true}
61+
var FIELDS_MARKING_UNHANDLED_TAGS = []string{FIELD_CLASS, FIELD_STRUCT, FIELD_NAMESPACE}
5962

6063
type CTagsParser struct {
6164
PrototypesField string
@@ -71,22 +74,20 @@ func (s *CTagsParser) Run(context map[string]interface{}) error {
7174
tags = append(tags, parseTag(row))
7275
}
7376

74-
tags = filterOutUnknownTags(tags)
75-
tags = filterOutTagsWithField(tags, FIELD_CLASS)
76-
tags = filterOutTagsWithField(tags, FIELD_STRUCT)
77-
tags = filterOutTagsWithField(tags, FIELD_NAMESPACE)
78-
tags = skipTagsWhere(tags, signatureContainsDefaultArg)
79-
tags = addPrototypes(tags)
80-
tags = removeDefinedProtypes(tags)
81-
tags = removeDuplicate(tags)
82-
tags = skipTagsWhere(tags, prototypeAndCodeDontMatch)
83-
84-
if len(tags) > 0 {
85-
line, err := strconv.Atoi(tags[0][FIELD_LINE])
86-
if err != nil {
87-
return utils.WrapError(err)
88-
}
89-
context[constants.CTX_FIRST_FUNCTION_AT_LINE] = line
77+
skipTagsWhere(tags, tagIsUnknown)
78+
skipTagsWithField(tags, FIELDS_MARKING_UNHANDLED_TAGS)
79+
skipTagsWhere(tags, signatureContainsDefaultArg)
80+
addPrototypes(tags)
81+
removeDefinedProtypes(tags)
82+
removeDuplicate(tags)
83+
skipTagsWhere(tags, prototypeAndCodeDontMatch)
84+
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
9091
}
9192

9293
prototypes := toPrototypes(tags)
@@ -96,22 +97,83 @@ func (s *CTagsParser) Run(context map[string]interface{}) error {
9697
return nil
9798
}
9899

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+
99160
func toPrototypes(tags []map[string]string) []*types.Prototype {
100161
prototypes := []*types.Prototype{}
101162
for _, tag := range tags {
102-
if tag[FIELD_SKIP] != "true" {
163+
if tag[FIELD_SKIP] != TRUE {
103164
ctag := types.Prototype{FunctionName: tag[FIELD_FUNCTION_NAME], Prototype: tag[KIND_PROTOTYPE], Modifiers: tag[KIND_PROTOTYPE_MODIFIERS], Fields: tag}
104165
prototypes = append(prototypes, &ctag)
105166
}
106167
}
107168
return prototypes
108169
}
109170

110-
func addPrototypes(tags []map[string]string) []map[string]string {
171+
func addPrototypes(tags []map[string]string) {
111172
for _, tag := range tags {
112-
addPrototype(tag)
173+
if tag[FIELD_SKIP] != TRUE {
174+
addPrototype(tag)
175+
}
113176
}
114-
return tags
115177
}
116178

117179
func addPrototype(tag map[string]string) {
@@ -135,55 +197,53 @@ func addPrototype(tag map[string]string) {
135197
tag[KIND_PROTOTYPE_MODIFIERS] = strings.TrimSpace(tag[KIND_PROTOTYPE_MODIFIERS])
136198
}
137199

138-
func removeDefinedProtypes(tags []map[string]string) []map[string]string {
200+
func removeDefinedProtypes(tags []map[string]string) {
139201
definedPrototypes := make(map[string]bool)
140202
for _, tag := range tags {
141203
if tag[FIELD_KIND] == KIND_PROTOTYPE {
142204
definedPrototypes[tag[KIND_PROTOTYPE]] = true
143205
}
144206
}
145207

146-
var newTags []map[string]string
147208
for _, tag := range tags {
148-
if !definedPrototypes[tag[KIND_PROTOTYPE]] {
149-
newTags = append(newTags, tag)
209+
if definedPrototypes[tag[KIND_PROTOTYPE]] {
210+
tag[FIELD_SKIP] = TRUE
150211
}
151212
}
152-
return newTags
153213
}
154214

155-
func removeDuplicate(tags []map[string]string) []map[string]string {
215+
func removeDuplicate(tags []map[string]string) {
156216
definedPrototypes := make(map[string]bool)
157217

158-
var newTags []map[string]string
159218
for _, tag := range tags {
160219
if !definedPrototypes[tag[KIND_PROTOTYPE]] {
161-
newTags = append(newTags, tag)
162220
definedPrototypes[tag[KIND_PROTOTYPE]] = true
221+
} else {
222+
tag[FIELD_SKIP] = TRUE
163223
}
164224
}
165-
return newTags
166225
}
167226

168227
type skipFuncType func(tag map[string]string) bool
169228

170-
func skipTagsWhere(tags []map[string]string, skipFuncs ...skipFuncType) []map[string]string {
229+
func skipTagsWhere(tags []map[string]string, skipFuncs ...skipFuncType) {
171230
for _, tag := range tags {
172-
skip := skipFuncs[0](tag)
173-
for _, skipFunc := range skipFuncs[1:] {
174-
skip = skip || skipFunc(tag)
231+
if tag[FIELD_SKIP] != TRUE {
232+
skip := skipFuncs[0](tag)
233+
for _, skipFunc := range skipFuncs[1:] {
234+
skip = skip || skipFunc(tag)
235+
}
236+
tag[FIELD_SKIP] = strconv.FormatBool(skip)
175237
}
176-
tag[FIELD_SKIP] = strconv.FormatBool(skip)
177238
}
178-
return tags
179239
}
180240

181241
func signatureContainsDefaultArg(tag map[string]string) bool {
182242
return strings.Contains(tag[FIELD_SIGNATURE], "=")
183243
}
184244

185245
func prototypeAndCodeDontMatch(tag map[string]string) bool {
186-
if tag[FIELD_SKIP] == "true" {
246+
if tag[FIELD_SKIP] == TRUE {
187247
return true
188248
}
189249

@@ -204,24 +264,25 @@ func removeSpacesAndTabs(s string) string {
204264
return s
205265
}
206266

207-
func filterOutTagsWithField(tags []map[string]string, field string) []map[string]string {
208-
var newTags []map[string]string
267+
func skipTagsWithField(tags []map[string]string, fields []string) {
209268
for _, tag := range tags {
210-
if tag[field] == constants.EMPTY_STRING {
211-
newTags = append(newTags, tag)
269+
if tagHasAtLeastOneField(tag, fields) {
270+
tag[FIELD_SKIP] = TRUE
212271
}
213272
}
214-
return newTags
215273
}
216274

217-
func filterOutUnknownTags(tags []map[string]string) []map[string]string {
218-
var newTags []map[string]string
219-
for _, tag := range tags {
220-
if KNOWN_TAG_KINDS[tag[FIELD_KIND]] {
221-
newTags = append(newTags, tag)
275+
func tagHasAtLeastOneField(tag map[string]string, fields []string) bool {
276+
for _, field := range fields {
277+
if tag[field] != constants.EMPTY_STRING {
278+
return true
222279
}
223280
}
224-
return newTags
281+
return false
282+
}
283+
284+
func tagIsUnknown(tag map[string]string) bool {
285+
return !KNOWN_TAG_KINDS[tag[FIELD_KIND]]
225286
}
226287

227288
func parseTag(row string) map[string]string {

‎src/arduino.cc/builder/prototypes_adder.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ func (s *PrototypesAdder) Run(context map[string]interface{}) error {
4343
source := context[constants.CTX_SOURCE].(string)
4444
sourceRows := strings.Split(source, "\n")
4545

46-
if !utils.MapHas(context, constants.CTX_FIRST_FUNCTION_AT_LINE) {
46+
if !utils.MapHas(context, constants.CTX_LINE_WHERE_TO_INSERT_PROTOTYPES) {
4747
return nil
4848
}
4949

50-
firstFunctionLine := context[constants.CTX_FIRST_FUNCTION_AT_LINE].(int)
50+
firstFunctionLine := context[constants.CTX_LINE_WHERE_TO_INSERT_PROTOTYPES].(int)
5151
if firstFunctionOutsideOfSource(firstFunctionLine, sourceRows) {
5252
return nil
5353
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
t1Callback /tmp/test547238273/preproc/ctags_target.cpp /^Task t1(&t1Callback);$/;" kind:variable line:2
2+
t1Callback /tmp/test547238273/preproc/ctags_target.cpp /^void t1Callback() {}$/;" kind:function line:3 signature:() returntype:void
3+
setup /tmp/test547238273/preproc/ctags_target.cpp /^void setup() {}$/;" kind:function line:4 signature:() returntype:void
4+
loop /tmp/test547238273/preproc/ctags_target.cpp /^void loop() {}$/;" kind:function line:5 signature:() returntype:void

0 commit comments

Comments
 (0)
Please sign in to comment.