@@ -18,6 +18,7 @@ import (
18
18
"go/scanner"
19
19
"go/token"
20
20
"path/filepath"
21
+ "sort"
21
22
"strconv"
22
23
"strings"
23
24
@@ -36,6 +37,7 @@ type cgoPackage struct {
36
37
fset * token.FileSet
37
38
tokenFiles map [string ]* token.File
38
39
definedGlobally map [string ]ast.Node
40
+ noescapingFuncs map [string ]* noescapingFunc // #cgo noescape lines
39
41
anonDecls map [interface {}]string
40
42
cflags []string // CFlags from #cgo lines
41
43
ldflags []string // LDFlags from #cgo lines
@@ -74,6 +76,13 @@ type bitfieldInfo struct {
74
76
endBit int64 // may be 0 meaning "until the end of the field"
75
77
}
76
78
79
+ // Information about a #cgo noescape line in the source code.
80
+ type noescapingFunc struct {
81
+ name string
82
+ pos token.Pos
83
+ used bool // true if used somewhere in the source (for proper error reporting)
84
+ }
85
+
77
86
// cgoAliases list type aliases between Go and C, for types that are equivalent
78
87
// in both languages. See addTypeAliases.
79
88
var cgoAliases = map [string ]string {
@@ -171,6 +180,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
171
180
fset : fset ,
172
181
tokenFiles : map [string ]* token.File {},
173
182
definedGlobally : map [string ]ast.Node {},
183
+ noescapingFuncs : map [string ]* noescapingFunc {},
174
184
anonDecls : map [interface {}]string {},
175
185
visitedFiles : map [string ][]byte {},
176
186
}
@@ -330,6 +340,22 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
330
340
})
331
341
}
332
342
343
+ // Show an error when a #cgo noescape line isn't used in practice.
344
+ // This matches upstream Go. I think the goal is to avoid issues with
345
+ // misspelled function names, which seems very useful.
346
+ var unusedNoescapeLines []* noescapingFunc
347
+ for _ , value := range p .noescapingFuncs {
348
+ if ! value .used {
349
+ unusedNoescapeLines = append (unusedNoescapeLines , value )
350
+ }
351
+ }
352
+ sort .SliceStable (unusedNoescapeLines , func (i , j int ) bool {
353
+ return unusedNoescapeLines [i ].pos < unusedNoescapeLines [j ].pos
354
+ })
355
+ for _ , value := range unusedNoescapeLines {
356
+ p .addError (value .pos , fmt .Sprintf ("function %#v not found in #cgo noescape line" , value .name ))
357
+ }
358
+
333
359
// Print the newly generated in-memory AST, for debugging.
334
360
//ast.Print(fset, p.generated)
335
361
@@ -395,6 +421,33 @@ func (p *cgoPackage) parseCGoPreprocessorLines(text string, pos token.Pos) strin
395
421
}
396
422
text = text [:lineStart ] + string (spaces ) + text [lineEnd :]
397
423
424
+ allFields := strings .Fields (line [4 :])
425
+ switch allFields [0 ] {
426
+ case "noescape" :
427
+ // The code indicates that pointer parameters will not be captured
428
+ // by the called C function.
429
+ if len (allFields ) < 2 {
430
+ p .addErrorAfter (pos , text [:lineStart ], "missing function name in #cgo noescape line" )
431
+ continue
432
+ }
433
+ if len (allFields ) > 2 {
434
+ p .addErrorAfter (pos , text [:lineStart ], "multiple function names in #cgo noescape line" )
435
+ continue
436
+ }
437
+ name := allFields [1 ]
438
+ p .noescapingFuncs [name ] = & noescapingFunc {
439
+ name : name ,
440
+ pos : pos ,
441
+ used : false ,
442
+ }
443
+ continue
444
+ case "nocallback" :
445
+ // We don't do anything special when calling a C function, so there
446
+ // appears to be no optimization that we can do here.
447
+ // Accept, but ignore the parameter for compatibility.
448
+ continue
449
+ }
450
+
398
451
// Get the text before the colon in the #cgo directive.
399
452
colon := strings .IndexByte (line , ':' )
400
453
if colon < 0 {
0 commit comments