@@ -55,22 +55,24 @@ type Info struct {
55
55
instanceSets * typeparams.PackageInstanceSets
56
56
HasPointer map [* types.Var ]bool
57
57
funcInstInfos * typeparams.InstanceMap [* FuncInfo ]
58
- funcLitInfos map [* ast.FuncLit ]* FuncInfo
58
+ funcLitInfos map [* ast.FuncLit ][] * FuncInfo
59
59
InitFuncInfo * FuncInfo // Context for package variable initialization.
60
60
61
61
isImportedBlocking func (typeparams.Instance ) bool // For functions from other packages.
62
62
allInfos []* FuncInfo
63
63
}
64
64
65
- func (info * Info ) newFuncInfo (n ast.Node , inst * typeparams.Instance ) * FuncInfo {
65
+ func (info * Info ) newFuncInfo (n ast.Node , obj types. Object , typeArgs typesutil. TypeList , resolver * typeparams.Resolver ) * FuncInfo {
66
66
funcInfo := & FuncInfo {
67
67
pkgInfo : info ,
68
68
Flattened : make (map [ast.Node ]bool ),
69
69
Blocking : make (map [ast.Node ]bool ),
70
70
GotoLabel : make (map [* types.Label ]bool ),
71
71
loopReturnIndex : - 1 ,
72
- localInstCallees : new (typeparams.InstanceMap [[]astPath ]),
72
+ instCallees : new (typeparams.InstanceMap [[]astPath ]),
73
73
literalFuncCallees : make (map [* ast.FuncLit ]astPath ),
74
+ typeArgs : typeArgs ,
75
+ resolver : resolver ,
74
76
}
75
77
76
78
// Register the function in the appropriate map.
@@ -86,13 +88,14 @@ func (info *Info) newFuncInfo(n ast.Node, inst *typeparams.Instance) *FuncInfo {
86
88
funcInfo .Blocking [n ] = true
87
89
}
88
90
89
- if inst == nil {
90
- inst = & typeparams. Instance { Object : info .Defs [n .Name ]}
91
+ if obj == nil {
92
+ obj = info .Defs [n .Name ]
91
93
}
92
- info .funcInstInfos .Set (* inst , funcInfo )
94
+ inst := typeparams.Instance {Object : obj , TArgs : typeArgs }
95
+ info .funcInstInfos .Set (inst , funcInfo )
93
96
94
97
case * ast.FuncLit :
95
- info .funcLitInfos [n ] = funcInfo
98
+ info .funcLitInfos [n ] = append ( info . funcLitInfos [ n ], funcInfo )
96
99
}
97
100
98
101
// And add it to the list of all functions.
@@ -105,28 +108,40 @@ func (info *Info) newFuncInfoInstances(fd *ast.FuncDecl) []*FuncInfo {
105
108
obj := info .Defs [fd .Name ]
106
109
instances := info .instanceSets .Pkg (info .Pkg ).ForObj (obj )
107
110
if len (instances ) == 0 {
108
- // No instances found, this is a non-generic function.
109
- return []* FuncInfo {info .newFuncInfo (fd , nil )}
111
+ if typeparams .HasTypeParams (obj .Type ()) {
112
+ // This is a generic function, but no instances were found,
113
+ // this is an unused function, so skip over it.
114
+ return []* FuncInfo {}
115
+ }
116
+
117
+ // No instances found and this is a non-generic function.
118
+ return []* FuncInfo {info .newFuncInfo (fd , nil , nil , nil )}
110
119
}
111
120
112
121
funcInfos := make ([]* FuncInfo , 0 , len (instances ))
113
122
for _ , inst := range instances {
114
- fi := info . newFuncInfo ( fd , & inst )
123
+ var resolver * typeparams. Resolver
115
124
if sig , ok := obj .Type ().(* types.Signature ); ok {
116
125
tp := typeparams .ToSlice (typeparams .SignatureTypeParams (sig ))
117
- fi . resolver = typeparams .NewResolver (info .typeCtx , tp , inst .TArgs )
126
+ resolver = typeparams .NewResolver (info .typeCtx , tp , inst .TArgs )
118
127
}
128
+ fi := info .newFuncInfo (fd , inst .Object , inst .TArgs , resolver )
119
129
funcInfos = append (funcInfos , fi )
120
130
}
121
131
return funcInfos
122
132
}
123
133
124
134
// IsBlocking returns true if the function may contain blocking calls or operations.
135
+ // If inst is from a different package, this will use the isImportedBlocking
136
+ // to lookup the information from the other package.
125
137
func (info * Info ) IsBlocking (inst typeparams.Instance ) bool {
138
+ if inst .Object .Pkg () != info .Pkg {
139
+ return info .isImportedBlocking (inst )
140
+ }
126
141
if funInfo := info .FuncInfo (inst ); funInfo != nil {
127
142
return funInfo .IsBlocking ()
128
143
}
129
- panic (fmt .Errorf (`info did not have function declaration instance for %q` , inst ))
144
+ panic (fmt .Errorf (`info did not have function declaration instance for %q` , inst . TypeString () ))
130
145
}
131
146
132
147
// FuncInfo returns information about the given function declaration instance, or nil if not found.
@@ -135,8 +150,16 @@ func (info *Info) FuncInfo(inst typeparams.Instance) *FuncInfo {
135
150
}
136
151
137
152
// FuncLitInfo returns information about the given function literal, or nil if not found.
138
- func (info * Info ) FuncLitInfo (fun * ast.FuncLit ) * FuncInfo {
139
- return info .funcLitInfos [fun ]
153
+ // The given type arguments are used to identify the correct instance of the
154
+ // function literal in the case the literal was defined inside a generic function.
155
+ func (info * Info ) FuncLitInfo (fun * ast.FuncLit , typeArgs typesutil.TypeList ) * FuncInfo {
156
+ lits := info .funcLitInfos [fun ]
157
+ for _ , fi := range lits {
158
+ if fi .typeArgs .Equal (typeArgs ) {
159
+ return fi
160
+ }
161
+ }
162
+ return nil
140
163
}
141
164
142
165
// VarsWithInitializers returns a set of package-level variables that have
@@ -160,9 +183,9 @@ func AnalyzePkg(files []*ast.File, fileSet *token.FileSet, typesInfo *types.Info
160
183
HasPointer : make (map [* types.Var ]bool ),
161
184
isImportedBlocking : isBlocking ,
162
185
funcInstInfos : new (typeparams.InstanceMap [* FuncInfo ]),
163
- funcLitInfos : make (map [* ast.FuncLit ]* FuncInfo ),
186
+ funcLitInfos : make (map [* ast.FuncLit ][] * FuncInfo ),
164
187
}
165
- info .InitFuncInfo = info .newFuncInfo (nil , nil )
188
+ info .InitFuncInfo = info .newFuncInfo (nil , nil , nil , nil )
166
189
167
190
// Traverse the full AST of the package and collect information about existing
168
191
// functions.
@@ -190,19 +213,19 @@ func (info *Info) propagateFunctionBlocking() bool {
190
213
done := true
191
214
for _ , caller := range info .allInfos {
192
215
// Check calls to named functions and function-typed variables.
193
- caller .localInstCallees .Iterate (func (callee typeparams.Instance , callSites []astPath ) {
194
- if info .FuncInfo ( callee ). IsBlocking () {
216
+ caller .instCallees .Iterate (func (callee typeparams.Instance , callSites []astPath ) {
217
+ if info .IsBlocking (callee ) {
195
218
for _ , callSite := range callSites {
196
219
caller .markBlocking (callSite )
197
220
}
198
- caller .localInstCallees .Delete (callee )
221
+ caller .instCallees .Delete (callee )
199
222
done = false
200
223
}
201
224
})
202
225
203
226
// Check direct calls to function literals.
204
227
for callee , callSite := range caller .literalFuncCallees {
205
- if info .FuncLitInfo (callee ).IsBlocking () {
228
+ if info .FuncLitInfo (callee , caller . typeArgs ).IsBlocking () {
206
229
caller .markBlocking (callSite )
207
230
delete (caller .literalFuncCallees , callee )
208
231
done = false
@@ -250,15 +273,18 @@ type FuncInfo struct {
250
273
// returns defined before any defers in a loop may still be affected by
251
274
// those defers because of the loop. See comment on [deferStmt].
252
275
loopReturnIndex int
253
- // List of other named functions from the current package this function calls.
276
+ // List of other named functions in the current package or another package
277
+ // that this function calls.
254
278
// If any of them are blocking, this function will become blocking too.
255
- localInstCallees * typeparams.InstanceMap [[]astPath ]
279
+ instCallees * typeparams.InstanceMap [[]astPath ]
256
280
// List of function literals directly called from this function (for example:
257
281
// `func() { /* do stuff */ }()`). This is distinct from function literals
258
282
// assigned to named variables (for example: `doStuff := func() {};
259
283
// doStuff()`), which are handled by localInstCallees. If any of them are
260
284
// identified as blocking, this function will become blocking too.
261
285
literalFuncCallees map [* ast.FuncLit ]astPath
286
+ // typeArgs are the type arguments for the function instance.
287
+ typeArgs typesutil.TypeList
262
288
// resolver is used by this function instance to resolve any type arguments
263
289
// for internal function calls.
264
290
// This may be nil if not an instance of a generic function.
@@ -276,6 +302,12 @@ func (fi *FuncInfo) IsBlocking() bool {
276
302
return fi == nil || len (fi .Blocking ) != 0
277
303
}
278
304
305
+ // TypeArgs gets the type arguments of this inside of a function instance
306
+ // or empty if not in a function instance.
307
+ func (fi * FuncInfo ) TypeArgs () typesutil.TypeList {
308
+ return fi .typeArgs
309
+ }
310
+
279
311
// propagateReturnBlocking updates the blocking on the return statements.
280
312
// See comment on [deferStmt].
281
313
//
@@ -341,7 +373,7 @@ func (fi *FuncInfo) Visit(node ast.Node) ast.Visitor {
341
373
return nil
342
374
case * ast.FuncLit :
343
375
// Analyze the function literal in its own context.
344
- return fi .pkgInfo .newFuncInfo (n , nil )
376
+ return fi .pkgInfo .newFuncInfo (n , nil , fi . typeArgs , fi . resolver )
345
377
case * ast.BranchStmt :
346
378
switch n .Tok {
347
379
case token .GOTO :
@@ -502,7 +534,7 @@ func (fi *FuncInfo) visitCallExpr(n *ast.CallExpr, deferredCall bool) ast.Visito
502
534
// Register literal function call site in case it is identified as blocking.
503
535
fi .literalFuncCallees [f ] = fi .visitorStack .copy ()
504
536
if deferredCall {
505
- fi .deferStmts = append (fi .deferStmts , newLitDefer (f ))
537
+ fi .deferStmts = append (fi .deferStmts , newLitDefer (f , fi . typeArgs ))
506
538
}
507
539
return nil // No need to walk under this CallExpr, we already did it manually.
508
540
case * ast.IndexExpr :
@@ -610,21 +642,11 @@ func (fi *FuncInfo) callToNamedFunc(callee typeparams.Instance, deferredCall boo
610
642
}
611
643
}
612
644
613
- if o .Pkg () != fi .pkgInfo .Pkg {
614
- if fi .pkgInfo .isImportedBlocking (callee ) {
615
- fi .markBlocking (fi .visitorStack )
616
- if deferredCall {
617
- fi .deferStmts = append (fi .deferStmts , newBlockingDefer ())
618
- }
619
- }
620
- return
621
- }
622
-
623
645
// We probably don't know yet whether the callee function is blocking.
624
646
// Record the calls site for the later stage.
625
- paths := fi .localInstCallees .Get (callee )
647
+ paths := fi .instCallees .Get (callee )
626
648
paths = append (paths , fi .visitorStack .copy ())
627
- fi .localInstCallees .Set (callee , paths )
649
+ fi .instCallees .Set (callee , paths )
628
650
if deferredCall {
629
651
fi .deferStmts = append (fi .deferStmts , newInstDefer (callee ))
630
652
}
0 commit comments