@@ -16,9 +16,11 @@ import (
16
16
17
17
"golang.org/x/tools/go/analysis"
18
18
"golang.org/x/tools/go/analysis/passes/inspect"
19
+ "golang.org/x/tools/go/ast/astutil"
19
20
"golang.org/x/tools/go/ast/inspector"
20
21
"golang.org/x/tools/go/cfg"
21
22
"golang.org/x/tools/go/types/typeutil"
23
+ "golang.org/x/tools/internal/typeparams"
22
24
)
23
25
24
26
var Analyzer = & analysis.Analyzer {
@@ -187,8 +189,12 @@ func (c *CFGs) callMayReturn(call *ast.CallExpr) (r bool) {
187
189
return false // panic never returns
188
190
}
189
191
190
- // Is this a static call?
191
- fn := typeutil .StaticCallee (c .pass .TypesInfo , call )
192
+ // Is this a static call? Also includes static functions
193
+ // parameterized by a type. Such functions may or may not
194
+ // return depending on the parameter type, but in some
195
+ // cases the answer is definite. We let ctrlflow figure
196
+ // that out.
197
+ fn := staticCallee (c .pass .TypesInfo , call )
192
198
if fn == nil {
193
199
return true // callee not statically known; be conservative
194
200
}
@@ -204,6 +210,50 @@ func (c *CFGs) callMayReturn(call *ast.CallExpr) (r bool) {
204
210
return ! c .pass .ImportObjectFact (fn , new (noReturn ))
205
211
}
206
212
213
+ // staticCallee returns static function, if any, called by call.
214
+ // Effectivelly reduces to typeutil.StaticCallee. In addition,
215
+ // returns static function parameterized by a type, if any.
216
+ //
217
+ // TODO(zpavlinovic): can this be replaced by typeutil.StaticCallee
218
+ // in the future?
219
+ func staticCallee (info * types.Info , call * ast.CallExpr ) * types.Func {
220
+ if fn := typeutil .StaticCallee (info , call ); fn != nil {
221
+ return fn
222
+ }
223
+ return staticTypeParamCallee (info , call )
224
+ }
225
+
226
+ // staticTypeParamCallee returns the static function in call, if any,
227
+ // expected to be parameterized by a type.
228
+ func staticTypeParamCallee (info * types.Info , call * ast.CallExpr ) * types.Func {
229
+ ix := typeparams .GetIndexExprData (astutil .Unparen (call .Fun ))
230
+ if ix == nil {
231
+ return nil
232
+ }
233
+
234
+ var obj types.Object
235
+ switch fun := ix .X .(type ) {
236
+ case * ast.Ident :
237
+ obj = info .Uses [fun ] // type, var, builtin, or declared func
238
+ case * ast.SelectorExpr :
239
+ if sel , ok := info .Selections [fun ]; ok {
240
+ obj = sel .Obj () // method or field
241
+ } else {
242
+ obj = info .Uses [fun .Sel ] // qualified identifier?
243
+ }
244
+ }
245
+
246
+ if f , ok := obj .(* types.Func ); ok && ! interfaceMethod (f ) {
247
+ return f
248
+ }
249
+ return nil
250
+ }
251
+
252
+ func interfaceMethod (f * types.Func ) bool {
253
+ recv := f .Type ().(* types.Signature ).Recv ()
254
+ return recv != nil && types .IsInterface (recv .Type ())
255
+ }
256
+
207
257
var panicBuiltin = types .Universe .Lookup ("panic" ).(* types.Builtin )
208
258
209
259
func hasReachableReturn (g * cfg.CFG ) bool {
0 commit comments