@@ -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
@@ -42,6 +43,7 @@ type cgoPackage struct {
42
43
fset * token.FileSet
43
44
tokenFiles map [string ]* token.File
44
45
definedGlobally map [string ]ast.Node
46
+ noescapingFuncs map [string ]* noescapingFunc // #cgo noescape lines
45
47
anonDecls map [interface {}]string
46
48
cflags []string // CFlags from #cgo lines
47
49
ldflags []string // LDFlags from #cgo lines
@@ -80,6 +82,13 @@ type bitfieldInfo struct {
80
82
endBit int64 // may be 0 meaning "until the end of the field"
81
83
}
82
84
85
+ // Information about a #cgo noescape line in the source code.
86
+ type noescapingFunc struct {
87
+ name string
88
+ pos token.Pos
89
+ used bool // true if used somewhere in the source (for proper error reporting)
90
+ }
91
+
83
92
// cgoAliases list type aliases between Go and C, for types that are equivalent
84
93
// in both languages. See addTypeAliases.
85
94
var cgoAliases = map [string ]string {
@@ -179,6 +188,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
179
188
fset : fset ,
180
189
tokenFiles : map [string ]* token.File {},
181
190
definedGlobally : map [string ]ast.Node {},
191
+ noescapingFuncs : map [string ]* noescapingFunc {},
182
192
anonDecls : map [interface {}]string {},
183
193
visitedFiles : map [string ][]byte {},
184
194
}
@@ -337,6 +347,22 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
337
347
})
338
348
}
339
349
350
+ // Show an error when a #cgo noescape line isn't used in practice.
351
+ // This matches upstream Go. I think the goal is to avoid issues with
352
+ // misspelled function names, which seems very useful.
353
+ var unusedNoescapeLines []* noescapingFunc
354
+ for _ , value := range p .noescapingFuncs {
355
+ if ! value .used {
356
+ unusedNoescapeLines = append (unusedNoescapeLines , value )
357
+ }
358
+ }
359
+ sort .SliceStable (unusedNoescapeLines , func (i , j int ) bool {
360
+ return unusedNoescapeLines [i ].pos < unusedNoescapeLines [j ].pos
361
+ })
362
+ for _ , value := range unusedNoescapeLines {
363
+ p .addError (value .pos , fmt .Sprintf ("function %#v not found in #cgo noescape line" , value .name ))
364
+ }
365
+
340
366
// Print the newly generated in-memory AST, for debugging.
341
367
//ast.Print(fset, p.generated)
342
368
@@ -402,6 +428,33 @@ func (p *cgoPackage) parseCGoPreprocessorLines(text string, pos token.Pos) strin
402
428
}
403
429
text = text [:lineStart ] + string (spaces ) + text [lineEnd :]
404
430
431
+ allFields := strings .Fields (line [4 :])
432
+ switch allFields [0 ] {
433
+ case "noescape" :
434
+ // The code indicates that pointer parameters will not be captured
435
+ // by the called C function.
436
+ if len (allFields ) < 2 {
437
+ p .addErrorAfter (pos , text [:lineStart ], "missing function name in #cgo noescape line" )
438
+ continue
439
+ }
440
+ if len (allFields ) > 2 {
441
+ p .addErrorAfter (pos , text [:lineStart ], "multiple function names in #cgo noescape line" )
442
+ continue
443
+ }
444
+ name := allFields [1 ]
445
+ p .noescapingFuncs [name ] = & noescapingFunc {
446
+ name : name ,
447
+ pos : pos ,
448
+ used : false ,
449
+ }
450
+ continue
451
+ case "nocallback" :
452
+ // We don't do anything special when calling a C function, so there
453
+ // appears to be no optimization that we can do here.
454
+ // Accept, but ignore the parameter for compatibility.
455
+ continue
456
+ }
457
+
405
458
// Get the text before the colon in the #cgo directive.
406
459
colon := strings .IndexByte (line , ':' )
407
460
if colon < 0 {
0 commit comments