@@ -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 {
@@ -186,6 +195,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
186
195
fset : fset ,
187
196
tokenFiles : map [string ]* token.File {},
188
197
definedGlobally : map [string ]ast.Node {},
198
+ noescapingFuncs : map [string ]* noescapingFunc {},
189
199
anonDecls : map [interface {}]string {},
190
200
visitedFiles : map [string ][]byte {},
191
201
}
@@ -344,6 +354,22 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
344
354
})
345
355
}
346
356
357
+ // Show an error when a #cgo noescape line isn't used in practice.
358
+ // This matches upstream Go. I think the goal is to avoid issues with
359
+ // misspelled function names, which seems very useful.
360
+ var unusedNoescapeLines []* noescapingFunc
361
+ for _ , value := range p .noescapingFuncs {
362
+ if ! value .used {
363
+ unusedNoescapeLines = append (unusedNoescapeLines , value )
364
+ }
365
+ }
366
+ sort .SliceStable (unusedNoescapeLines , func (i , j int ) bool {
367
+ return unusedNoescapeLines [i ].pos < unusedNoescapeLines [j ].pos
368
+ })
369
+ for _ , value := range unusedNoescapeLines {
370
+ p .addError (value .pos , fmt .Sprintf ("function %#v in #cgo noescape line is not used" , value .name ))
371
+ }
372
+
347
373
// Print the newly generated in-memory AST, for debugging.
348
374
//ast.Print(fset, p.generated)
349
375
@@ -409,6 +435,33 @@ func (p *cgoPackage) parseCGoPreprocessorLines(text string, pos token.Pos) strin
409
435
}
410
436
text = text [:lineStart ] + string (spaces ) + text [lineEnd :]
411
437
438
+ allFields := strings .Fields (line [4 :])
439
+ switch allFields [0 ] {
440
+ case "noescape" :
441
+ // The code indicates that pointer parameters will not be captured
442
+ // by the called C function.
443
+ if len (allFields ) < 2 {
444
+ p .addErrorAfter (pos , text [:lineStart ], "missing function name in #cgo noescape line" )
445
+ continue
446
+ }
447
+ if len (allFields ) > 2 {
448
+ p .addErrorAfter (pos , text [:lineStart ], "multiple function names in #cgo noescape line" )
449
+ continue
450
+ }
451
+ name := allFields [1 ]
452
+ p .noescapingFuncs [name ] = & noescapingFunc {
453
+ name : name ,
454
+ pos : pos ,
455
+ used : false ,
456
+ }
457
+ continue
458
+ case "nocallback" :
459
+ // We don't do anything special when calling a C function, so there
460
+ // appears to be no optimization that we can do here.
461
+ // Accept, but ignore the parameter for compatibility.
462
+ continue
463
+ }
464
+
412
465
// Get the text before the colon in the #cgo directive.
413
466
colon := strings .IndexByte (line , ':' )
414
467
if colon < 0 {
0 commit comments