diff options
Diffstat (limited to 'vendor/honnef.co/go/tools/unused/unused.go')
-rw-r--r-- | vendor/honnef.co/go/tools/unused/unused.go | 1964 |
1 files changed, 1964 insertions, 0 deletions
diff --git a/vendor/honnef.co/go/tools/unused/unused.go b/vendor/honnef.co/go/tools/unused/unused.go new file mode 100644 index 0000000..152d369 --- /dev/null +++ b/vendor/honnef.co/go/tools/unused/unused.go | |||
@@ -0,0 +1,1964 @@ | |||
1 | package unused | ||
2 | |||
3 | import ( | ||
4 | "fmt" | ||
5 | "go/ast" | ||
6 | "go/token" | ||
7 | "go/types" | ||
8 | "io" | ||
9 | "strings" | ||
10 | "sync" | ||
11 | "sync/atomic" | ||
12 | |||
13 | "golang.org/x/tools/go/analysis" | ||
14 | "honnef.co/go/tools/go/types/typeutil" | ||
15 | "honnef.co/go/tools/internal/passes/buildssa" | ||
16 | "honnef.co/go/tools/lint" | ||
17 | "honnef.co/go/tools/lint/lintdsl" | ||
18 | "honnef.co/go/tools/ssa" | ||
19 | ) | ||
20 | |||
21 | // The graph we construct omits nodes along a path that do not | ||
22 | // contribute any new information to the solution. For example, the | ||
23 | // full graph for a function with a receiver would be Func -> | ||
24 | // Signature -> Var -> Type. However, since signatures cannot be | ||
25 | // unused, and receivers are always considered used, we can compact | ||
26 | // the graph down to Func -> Type. This makes the graph smaller, but | ||
27 | // harder to debug. | ||
28 | |||
29 | // TODO(dh): conversions between structs mark fields as used, but the | ||
30 | // conversion itself isn't part of that subgraph. even if the function | ||
31 | // containing the conversion is unused, the fields will be marked as | ||
32 | // used. | ||
33 | |||
34 | // TODO(dh): we cannot observe function calls in assembly files. | ||
35 | |||
36 | /* | ||
37 | |||
38 | - packages use: | ||
39 | - (1.1) exported named types (unless in package main) | ||
40 | - (1.2) exported functions (unless in package main) | ||
41 | - (1.3) exported variables (unless in package main) | ||
42 | - (1.4) exported constants (unless in package main) | ||
43 | - (1.5) init functions | ||
44 | - (1.6) functions exported to cgo | ||
45 | - (1.7) the main function iff in the main package | ||
46 | - (1.8) symbols linked via go:linkname | ||
47 | |||
48 | - named types use: | ||
49 | - (2.1) exported methods | ||
50 | - (2.2) the type they're based on | ||
51 | - (2.3) all their aliases. we can't easily track uses of aliases | ||
52 | because go/types turns them into uses of the aliased types. assume | ||
53 | that if a type is used, so are all of its aliases. | ||
54 | - (2.4) the pointer type. this aids with eagerly implementing | ||
55 | interfaces. if a method that implements an interface is defined on | ||
56 | a pointer receiver, and the pointer type is never used, but the | ||
57 | named type is, then we still want to mark the method as used. | ||
58 | |||
59 | - variables and constants use: | ||
60 | - their types | ||
61 | |||
62 | - functions use: | ||
63 | - (4.1) all their arguments, return parameters and receivers | ||
64 | - (4.2) anonymous functions defined beneath them | ||
65 | - (4.3) closures and bound methods. | ||
66 | this implements a simplified model where a function is used merely by being referenced, even if it is never called. | ||
67 | that way we don't have to keep track of closures escaping functions. | ||
68 | - (4.4) functions they return. we assume that someone else will call the returned function | ||
69 | - (4.5) functions/interface methods they call | ||
70 | - types they instantiate or convert to | ||
71 | - (4.7) fields they access | ||
72 | - (4.8) types of all instructions | ||
73 | - (4.9) package-level variables they assign to iff in tests (sinks for benchmarks) | ||
74 | |||
75 | - conversions use: | ||
76 | - (5.1) when converting between two equivalent structs, the fields in | ||
77 | either struct use each other. the fields are relevant for the | ||
78 | conversion, but only if the fields are also accessed outside the | ||
79 | conversion. | ||
80 | - (5.2) when converting to or from unsafe.Pointer, mark all fields as used. | ||
81 | |||
82 | - structs use: | ||
83 | - (6.1) fields of type NoCopy sentinel | ||
84 | - (6.2) exported fields | ||
85 | - (6.3) embedded fields that help implement interfaces (either fully implements it, or contributes required methods) (recursively) | ||
86 | - (6.4) embedded fields that have exported methods (recursively) | ||
87 | - (6.5) embedded structs that have exported fields (recursively) | ||
88 | |||
89 | - (7.1) field accesses use fields | ||
90 | - (7.2) fields use their types | ||
91 | |||
92 | - (8.0) How we handle interfaces: | ||
93 | - (8.1) We do not technically care about interfaces that only consist of | ||
94 | exported methods. Exported methods on concrete types are always | ||
95 | marked as used. | ||
96 | - Any concrete type implements all known interfaces. Even if it isn't | ||
97 | assigned to any interfaces in our code, the user may receive a value | ||
98 | of the type and expect to pass it back to us through an interface. | ||
99 | |||
100 | Concrete types use their methods that implement interfaces. If the | ||
101 | type is used, it uses those methods. Otherwise, it doesn't. This | ||
102 | way, types aren't incorrectly marked reachable through the edge | ||
103 | from method to type. | ||
104 | |||
105 | - (8.3) All interface methods are marked as used, even if they never get | ||
106 | called. This is to accomodate sum types (unexported interface | ||
107 | method that must exist but never gets called.) | ||
108 | |||
109 | - (8.4) All embedded interfaces are marked as used. This is an | ||
110 | extension of 8.3, but we have to explicitly track embedded | ||
111 | interfaces because in a chain C->B->A, B wouldn't be marked as | ||
112 | used by 8.3 just because it contributes A's methods to C. | ||
113 | |||
114 | - Inherent uses: | ||
115 | - thunks and other generated wrappers call the real function | ||
116 | - (9.2) variables use their types | ||
117 | - (9.3) types use their underlying and element types | ||
118 | - (9.4) conversions use the type they convert to | ||
119 | - (9.5) instructions use their operands | ||
120 | - (9.6) instructions use their operands' types | ||
121 | - (9.7) variable _reads_ use variables, writes do not, except in tests | ||
122 | - (9.8) runtime functions that may be called from user code via the compiler | ||
123 | |||
124 | |||
125 | - const groups: | ||
126 | (10.1) if one constant out of a block of constants is used, mark all | ||
127 | of them used. a lot of the time, unused constants exist for the sake | ||
128 | of completeness. See also | ||
129 | https://github.com/dominikh/go-tools/issues/365 | ||
130 | |||
131 | |||
132 | - (11.1) anonymous struct types use all their fields. we cannot | ||
133 | deduplicate struct types, as that leads to order-dependent | ||
134 | reportings. we can't not deduplicate struct types while still | ||
135 | tracking fields, because then each instance of the unnamed type in | ||
136 | the data flow chain will get its own fields, causing false | ||
137 | positives. Thus, we only accurately track fields of named struct | ||
138 | types, and assume that unnamed struct types use all their fields. | ||
139 | |||
140 | |||
141 | - Differences in whole program mode: | ||
142 | - (e2) types aim to implement all exported interfaces from all packages | ||
143 | - (e3) exported identifiers aren't automatically used. for fields and | ||
144 | methods this poses extra issues due to reflection. We assume | ||
145 | that all exported fields are used. We also maintain a list of | ||
146 | known reflection-based method callers. | ||
147 | |||
148 | */ | ||
149 | |||
150 | func assert(b bool) { | ||
151 | if !b { | ||
152 | panic("failed assertion") | ||
153 | } | ||
154 | } | ||
155 | |||
156 | func typString(obj types.Object) string { | ||
157 | switch obj := obj.(type) { | ||
158 | case *types.Func: | ||
159 | return "func" | ||
160 | case *types.Var: | ||
161 | if obj.IsField() { | ||
162 | return "field" | ||
163 | } | ||
164 | return "var" | ||
165 | case *types.Const: | ||
166 | return "const" | ||
167 | case *types.TypeName: | ||
168 | return "type" | ||
169 | default: | ||
170 | return "identifier" | ||
171 | } | ||
172 | } | ||
173 | |||
174 | // /usr/lib/go/src/runtime/proc.go:433:6: func badmorestackg0 is unused (U1000) | ||
175 | |||
176 | // Functions defined in the Go runtime that may be called through | ||
177 | // compiler magic or via assembly. | ||
178 | var runtimeFuncs = map[string]bool{ | ||
179 | // The first part of the list is copied from | ||
180 | // cmd/compile/internal/gc/builtin.go, var runtimeDecls | ||
181 | "newobject": true, | ||
182 | "panicindex": true, | ||
183 | "panicslice": true, | ||
184 | "panicdivide": true, | ||
185 | "panicmakeslicelen": true, | ||
186 | "throwinit": true, | ||
187 | "panicwrap": true, | ||
188 | "gopanic": true, | ||
189 | "gorecover": true, | ||
190 | "goschedguarded": true, | ||
191 | "printbool": true, | ||
192 | "printfloat": true, | ||
193 | "printint": true, | ||
194 | "printhex": true, | ||
195 | "printuint": true, | ||
196 | "printcomplex": true, | ||
197 | "printstring": true, | ||
198 | "printpointer": true, | ||
199 | "printiface": true, | ||
200 | "printeface": true, | ||
201 | "printslice": true, | ||
202 | "printnl": true, | ||
203 | "printsp": true, | ||
204 | "printlock": true, | ||
205 | "printunlock": true, | ||
206 | "concatstring2": true, | ||
207 | "concatstring3": true, | ||
208 | "concatstring4": true, | ||
209 | "concatstring5": true, | ||
210 | "concatstrings": true, | ||
211 | "cmpstring": true, | ||
212 | "intstring": true, | ||
213 | "slicebytetostring": true, | ||
214 | "slicebytetostringtmp": true, | ||
215 | "slicerunetostring": true, | ||
216 | "stringtoslicebyte": true, | ||
217 | "stringtoslicerune": true, | ||
218 | "slicecopy": true, | ||
219 | "slicestringcopy": true, | ||
220 | "decoderune": true, | ||
221 | "countrunes": true, | ||
222 | "convI2I": true, | ||
223 | "convT16": true, | ||
224 | "convT32": true, | ||
225 | "convT64": true, | ||
226 | "convTstring": true, | ||
227 | "convTslice": true, | ||
228 | "convT2E": true, | ||
229 | "convT2Enoptr": true, | ||
230 | "convT2I": true, | ||
231 | "convT2Inoptr": true, | ||
232 | "assertE2I": true, | ||
233 | "assertE2I2": true, | ||
234 | "assertI2I": true, | ||
235 | "assertI2I2": true, | ||
236 | "panicdottypeE": true, | ||
237 | "panicdottypeI": true, | ||
238 | "panicnildottype": true, | ||
239 | "ifaceeq": true, | ||
240 | "efaceeq": true, | ||
241 | "fastrand": true, | ||
242 | "makemap64": true, | ||
243 | "makemap": true, | ||
244 | "makemap_small": true, | ||
245 | "mapaccess1": true, | ||
246 | "mapaccess1_fast32": true, | ||
247 | "mapaccess1_fast64": true, | ||
248 | "mapaccess1_faststr": true, | ||
249 | "mapaccess1_fat": true, | ||
250 | "mapaccess2": true, | ||
251 | "mapaccess2_fast32": true, | ||
252 | "mapaccess2_fast64": true, | ||
253 | "mapaccess2_faststr": true, | ||
254 | "mapaccess2_fat": true, | ||
255 | "mapassign": true, | ||
256 | "mapassign_fast32": true, | ||
257 | "mapassign_fast32ptr": true, | ||
258 | "mapassign_fast64": true, | ||
259 | "mapassign_fast64ptr": true, | ||
260 | "mapassign_faststr": true, | ||
261 | "mapiterinit": true, | ||
262 | "mapdelete": true, | ||
263 | "mapdelete_fast32": true, | ||
264 | "mapdelete_fast64": true, | ||
265 | "mapdelete_faststr": true, | ||
266 | "mapiternext": true, | ||
267 | "mapclear": true, | ||
268 | "makechan64": true, | ||
269 | "makechan": true, | ||
270 | "chanrecv1": true, | ||
271 | "chanrecv2": true, | ||
272 | "chansend1": true, | ||
273 | "closechan": true, | ||
274 | "writeBarrier": true, | ||
275 | "typedmemmove": true, | ||
276 | "typedmemclr": true, | ||
277 | "typedslicecopy": true, | ||
278 | "selectnbsend": true, | ||
279 | "selectnbrecv": true, | ||
280 | "selectnbrecv2": true, | ||
281 | "selectsetpc": true, | ||
282 | "selectgo": true, | ||
283 | "block": true, | ||
284 | "makeslice": true, | ||
285 | "makeslice64": true, | ||
286 | "growslice": true, | ||
287 | "memmove": true, | ||
288 | "memclrNoHeapPointers": true, | ||
289 | "memclrHasPointers": true, | ||
290 | "memequal": true, | ||
291 | "memequal8": true, | ||
292 | "memequal16": true, | ||
293 | "memequal32": true, | ||
294 | "memequal64": true, | ||
295 | "memequal128": true, | ||
296 | "int64div": true, | ||
297 | "uint64div": true, | ||
298 | "int64mod": true, | ||
299 | "uint64mod": true, | ||
300 | "float64toint64": true, | ||
301 | "float64touint64": true, | ||
302 | "float64touint32": true, | ||
303 | "int64tofloat64": true, | ||
304 | "uint64tofloat64": true, | ||
305 | "uint32tofloat64": true, | ||
306 | "complex128div": true, | ||
307 | "racefuncenter": true, | ||
308 | "racefuncenterfp": true, | ||
309 | "racefuncexit": true, | ||
310 | "raceread": true, | ||
311 | "racewrite": true, | ||
312 | "racereadrange": true, | ||
313 | "racewriterange": true, | ||
314 | "msanread": true, | ||
315 | "msanwrite": true, | ||
316 | "x86HasPOPCNT": true, | ||
317 | "x86HasSSE41": true, | ||
318 | "arm64HasATOMICS": true, | ||
319 | |||
320 | // The second part of the list is extracted from assembly code in | ||
321 | // the standard library, with the exception of the runtime package itself | ||
322 | "abort": true, | ||
323 | "aeshashbody": true, | ||
324 | "args": true, | ||
325 | "asminit": true, | ||
326 | "badctxt": true, | ||
327 | "badmcall2": true, | ||
328 | "badmcall": true, | ||
329 | "badmorestackg0": true, | ||
330 | "badmorestackgsignal": true, | ||
331 | "badsignal2": true, | ||
332 | "callbackasm1": true, | ||
333 | "callCfunction": true, | ||
334 | "cgocallback_gofunc": true, | ||
335 | "cgocallbackg": true, | ||
336 | "checkgoarm": true, | ||
337 | "check": true, | ||
338 | "debugCallCheck": true, | ||
339 | "debugCallWrap": true, | ||
340 | "emptyfunc": true, | ||
341 | "entersyscall": true, | ||
342 | "exit": true, | ||
343 | "exits": true, | ||
344 | "exitsyscall": true, | ||
345 | "externalthreadhandler": true, | ||
346 | "findnull": true, | ||
347 | "goexit1": true, | ||
348 | "gostring": true, | ||
349 | "i386_set_ldt": true, | ||
350 | "_initcgo": true, | ||
351 | "init_thread_tls": true, | ||
352 | "ldt0setup": true, | ||
353 | "libpreinit": true, | ||
354 | "load_g": true, | ||
355 | "morestack": true, | ||
356 | "mstart": true, | ||
357 | "nacl_sysinfo": true, | ||
358 | "nanotimeQPC": true, | ||
359 | "nanotime": true, | ||
360 | "newosproc0": true, | ||
361 | "newproc": true, | ||
362 | "newstack": true, | ||
363 | "noted": true, | ||
364 | "nowQPC": true, | ||
365 | "osinit": true, | ||
366 | "printf": true, | ||
367 | "racecallback": true, | ||
368 | "reflectcallmove": true, | ||
369 | "reginit": true, | ||
370 | "rt0_go": true, | ||
371 | "save_g": true, | ||
372 | "schedinit": true, | ||
373 | "setldt": true, | ||
374 | "settls": true, | ||
375 | "sighandler": true, | ||
376 | "sigprofNonGo": true, | ||
377 | "sigtrampgo": true, | ||
378 | "_sigtramp": true, | ||
379 | "sigtramp": true, | ||
380 | "stackcheck": true, | ||
381 | "syscall_chdir": true, | ||
382 | "syscall_chroot": true, | ||
383 | "syscall_close": true, | ||
384 | "syscall_dup2": true, | ||
385 | "syscall_execve": true, | ||
386 | "syscall_exit": true, | ||
387 | "syscall_fcntl": true, | ||
388 | "syscall_forkx": true, | ||
389 | "syscall_gethostname": true, | ||
390 | "syscall_getpid": true, | ||
391 | "syscall_ioctl": true, | ||
392 | "syscall_pipe": true, | ||
393 | "syscall_rawsyscall6": true, | ||
394 | "syscall_rawSyscall6": true, | ||
395 | "syscall_rawsyscall": true, | ||
396 | "syscall_RawSyscall": true, | ||
397 | "syscall_rawsysvicall6": true, | ||
398 | "syscall_setgid": true, | ||
399 | "syscall_setgroups": true, | ||
400 | "syscall_setpgid": true, | ||
401 | "syscall_setsid": true, | ||
402 | "syscall_setuid": true, | ||
403 | "syscall_syscall6": true, | ||
404 | "syscall_syscall": true, | ||
405 | "syscall_Syscall": true, | ||
406 | "syscall_sysvicall6": true, | ||
407 | "syscall_wait4": true, | ||
408 | "syscall_write": true, | ||
409 | "traceback": true, | ||
410 | "tstart": true, | ||
411 | "usplitR0": true, | ||
412 | "wbBufFlush": true, | ||
413 | "write": true, | ||
414 | } | ||
415 | |||
416 | type pkg struct { | ||
417 | Fset *token.FileSet | ||
418 | Files []*ast.File | ||
419 | Pkg *types.Package | ||
420 | TypesInfo *types.Info | ||
421 | TypesSizes types.Sizes | ||
422 | SSA *ssa.Package | ||
423 | SrcFuncs []*ssa.Function | ||
424 | } | ||
425 | |||
426 | type Checker struct { | ||
427 | WholeProgram bool | ||
428 | Debug io.Writer | ||
429 | |||
430 | mu sync.Mutex | ||
431 | initialPackages map[*types.Package]struct{} | ||
432 | allPackages map[*types.Package]struct{} | ||
433 | graph *Graph | ||
434 | } | ||
435 | |||
436 | func NewChecker(wholeProgram bool) *Checker { | ||
437 | return &Checker{ | ||
438 | initialPackages: map[*types.Package]struct{}{}, | ||
439 | allPackages: map[*types.Package]struct{}{}, | ||
440 | WholeProgram: wholeProgram, | ||
441 | } | ||
442 | } | ||
443 | |||
444 | func (c *Checker) Analyzer() *analysis.Analyzer { | ||
445 | name := "U1000" | ||
446 | if c.WholeProgram { | ||
447 | name = "U1001" | ||
448 | } | ||
449 | return &analysis.Analyzer{ | ||
450 | Name: name, | ||
451 | Doc: "Unused code", | ||
452 | Run: c.Run, | ||
453 | Requires: []*analysis.Analyzer{buildssa.Analyzer}, | ||
454 | } | ||
455 | } | ||
456 | |||
457 | func (c *Checker) Run(pass *analysis.Pass) (interface{}, error) { | ||
458 | c.mu.Lock() | ||
459 | if c.graph == nil { | ||
460 | c.graph = NewGraph() | ||
461 | c.graph.wholeProgram = c.WholeProgram | ||
462 | c.graph.fset = pass.Fset | ||
463 | } | ||
464 | |||
465 | var visit func(pkg *types.Package) | ||
466 | visit = func(pkg *types.Package) { | ||
467 | if _, ok := c.allPackages[pkg]; ok { | ||
468 | return | ||
469 | } | ||
470 | c.allPackages[pkg] = struct{}{} | ||
471 | for _, imp := range pkg.Imports() { | ||
472 | visit(imp) | ||
473 | } | ||
474 | } | ||
475 | visit(pass.Pkg) | ||
476 | |||
477 | c.initialPackages[pass.Pkg] = struct{}{} | ||
478 | c.mu.Unlock() | ||
479 | |||
480 | ssapkg := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) | ||
481 | pkg := &pkg{ | ||
482 | Fset: pass.Fset, | ||
483 | Files: pass.Files, | ||
484 | Pkg: pass.Pkg, | ||
485 | TypesInfo: pass.TypesInfo, | ||
486 | TypesSizes: pass.TypesSizes, | ||
487 | SSA: ssapkg.Pkg, | ||
488 | SrcFuncs: ssapkg.SrcFuncs, | ||
489 | } | ||
490 | |||
491 | c.processPkg(c.graph, pkg) | ||
492 | |||
493 | return nil, nil | ||
494 | } | ||
495 | |||
496 | func (c *Checker) ProblemObject(fset *token.FileSet, obj types.Object) lint.Problem { | ||
497 | name := obj.Name() | ||
498 | if sig, ok := obj.Type().(*types.Signature); ok && sig.Recv() != nil { | ||
499 | switch sig.Recv().Type().(type) { | ||
500 | case *types.Named, *types.Pointer: | ||
501 | typ := types.TypeString(sig.Recv().Type(), func(*types.Package) string { return "" }) | ||
502 | if len(typ) > 0 && typ[0] == '*' { | ||
503 | name = fmt.Sprintf("(%s).%s", typ, obj.Name()) | ||
504 | } else if len(typ) > 0 { | ||
505 | name = fmt.Sprintf("%s.%s", typ, obj.Name()) | ||
506 | } | ||
507 | } | ||
508 | } | ||
509 | |||
510 | checkName := "U1000" | ||
511 | if c.WholeProgram { | ||
512 | checkName = "U1001" | ||
513 | } | ||
514 | return lint.Problem{ | ||
515 | Pos: lint.DisplayPosition(fset, obj.Pos()), | ||
516 | Message: fmt.Sprintf("%s %s is unused", typString(obj), name), | ||
517 | Check: checkName, | ||
518 | } | ||
519 | } | ||
520 | |||
521 | func (c *Checker) Result() []types.Object { | ||
522 | out := c.results() | ||
523 | |||
524 | out2 := make([]types.Object, 0, len(out)) | ||
525 | for _, v := range out { | ||
526 | if _, ok := c.initialPackages[v.Pkg()]; !ok { | ||
527 | continue | ||
528 | } | ||
529 | out2 = append(out2, v) | ||
530 | } | ||
531 | |||
532 | return out2 | ||
533 | } | ||
534 | |||
535 | func (c *Checker) debugf(f string, v ...interface{}) { | ||
536 | if c.Debug != nil { | ||
537 | fmt.Fprintf(c.Debug, f, v...) | ||
538 | } | ||
539 | } | ||
540 | |||
541 | func (graph *Graph) quieten(node *Node) { | ||
542 | if node.seen { | ||
543 | return | ||
544 | } | ||
545 | switch obj := node.obj.(type) { | ||
546 | case *types.Named: | ||
547 | for i := 0; i < obj.NumMethods(); i++ { | ||
548 | m := obj.Method(i) | ||
549 | if node, ok := graph.nodeMaybe(m); ok { | ||
550 | node.quiet = true | ||
551 | } | ||
552 | } | ||
553 | case *types.Struct: | ||
554 | for i := 0; i < obj.NumFields(); i++ { | ||
555 | if node, ok := graph.nodeMaybe(obj.Field(i)); ok { | ||
556 | node.quiet = true | ||
557 | } | ||
558 | } | ||
559 | case *types.Interface: | ||
560 | for i := 0; i < obj.NumExplicitMethods(); i++ { | ||
561 | m := obj.ExplicitMethod(i) | ||
562 | if node, ok := graph.nodeMaybe(m); ok { | ||
563 | node.quiet = true | ||
564 | } | ||
565 | } | ||
566 | } | ||
567 | } | ||
568 | |||
569 | func (c *Checker) results() []types.Object { | ||
570 | if c.graph == nil { | ||
571 | // We never analyzed any packages | ||
572 | return nil | ||
573 | } | ||
574 | |||
575 | var out []types.Object | ||
576 | |||
577 | if c.WholeProgram { | ||
578 | var ifaces []*types.Interface | ||
579 | var notIfaces []types.Type | ||
580 | |||
581 | // implement as many interfaces as possible | ||
582 | c.graph.seenTypes.Iterate(func(t types.Type, _ interface{}) { | ||
583 | switch t := t.(type) { | ||
584 | case *types.Interface: | ||
585 | if t.NumMethods() > 0 { | ||
586 | ifaces = append(ifaces, t) | ||
587 | } | ||
588 | default: | ||
589 | if _, ok := t.Underlying().(*types.Interface); !ok { | ||
590 | notIfaces = append(notIfaces, t) | ||
591 | } | ||
592 | } | ||
593 | }) | ||
594 | |||
595 | for pkg := range c.allPackages { | ||
596 | for _, iface := range interfacesFromExportData(pkg) { | ||
597 | if iface.NumMethods() > 0 { | ||
598 | ifaces = append(ifaces, iface) | ||
599 | } | ||
600 | } | ||
601 | } | ||
602 | |||
603 | ctx := &context{ | ||
604 | g: c.graph, | ||
605 | seenTypes: &c.graph.seenTypes, | ||
606 | } | ||
607 | // (8.0) handle interfaces | ||
608 | // (e2) types aim to implement all exported interfaces from all packages | ||
609 | for _, t := range notIfaces { | ||
610 | // OPT(dh): it is unfortunate that we do not have access | ||
611 | // to a populated method set at this point. | ||
612 | ms := types.NewMethodSet(t) | ||
613 | for _, iface := range ifaces { | ||
614 | if sels, ok := c.graph.implements(t, iface, ms); ok { | ||
615 | for _, sel := range sels { | ||
616 | c.graph.useMethod(ctx, t, sel, t, edgeImplements) | ||
617 | } | ||
618 | } | ||
619 | } | ||
620 | } | ||
621 | } | ||
622 | |||
623 | if c.Debug != nil { | ||
624 | debugNode := func(node *Node) { | ||
625 | if node.obj == nil { | ||
626 | c.debugf("n%d [label=\"Root\"];\n", node.id) | ||
627 | } else { | ||
628 | c.debugf("n%d [label=%q];\n", node.id, fmt.Sprintf("(%T) %s", node.obj, node.obj)) | ||
629 | } | ||
630 | for _, e := range node.used { | ||
631 | for i := edgeKind(1); i < 64; i++ { | ||
632 | if e.kind.is(1 << i) { | ||
633 | c.debugf("n%d -> n%d [label=%q];\n", node.id, e.node.id, edgeKind(1<<i)) | ||
634 | } | ||
635 | } | ||
636 | } | ||
637 | } | ||
638 | |||
639 | c.debugf("digraph{\n") | ||
640 | debugNode(c.graph.Root) | ||
641 | c.graph.Nodes.Range(func(k, v interface{}) bool { | ||
642 | debugNode(v.(*Node)) | ||
643 | return true | ||
644 | }) | ||
645 | c.graph.TypeNodes.Iterate(func(key types.Type, value interface{}) { | ||
646 | debugNode(value.(*Node)) | ||
647 | }) | ||
648 | |||
649 | c.debugf("}\n") | ||
650 | } | ||
651 | |||
652 | c.graph.color(c.graph.Root) | ||
653 | // if a node is unused, don't report any of the node's | ||
654 | // children as unused. for example, if a function is unused, | ||
655 | // don't flag its receiver. if a named type is unused, don't | ||
656 | // flag its methods. | ||
657 | |||
658 | c.graph.Nodes.Range(func(k, v interface{}) bool { | ||
659 | c.graph.quieten(v.(*Node)) | ||
660 | return true | ||
661 | }) | ||
662 | c.graph.TypeNodes.Iterate(func(_ types.Type, value interface{}) { | ||
663 | c.graph.quieten(value.(*Node)) | ||
664 | }) | ||
665 | |||
666 | report := func(node *Node) { | ||
667 | if node.seen { | ||
668 | return | ||
669 | } | ||
670 | if node.quiet { | ||
671 | c.debugf("n%d [color=purple];\n", node.id) | ||
672 | return | ||
673 | } | ||
674 | |||
675 | c.debugf("n%d [color=red];\n", node.id) | ||
676 | switch obj := node.obj.(type) { | ||
677 | case *types.Var: | ||
678 | // don't report unnamed variables (interface embedding) | ||
679 | if obj.Name() != "" || obj.IsField() { | ||
680 | out = append(out, obj) | ||
681 | } | ||
682 | return | ||
683 | case types.Object: | ||
684 | if obj.Name() != "_" { | ||
685 | out = append(out, obj) | ||
686 | } | ||
687 | return | ||
688 | } | ||
689 | c.debugf("n%d [color=gray];\n", node.id) | ||
690 | } | ||
691 | c.graph.Nodes.Range(func(k, v interface{}) bool { | ||
692 | report(v.(*Node)) | ||
693 | return true | ||
694 | }) | ||
695 | c.graph.TypeNodes.Iterate(func(_ types.Type, value interface{}) { | ||
696 | report(value.(*Node)) | ||
697 | }) | ||
698 | |||
699 | return out | ||
700 | } | ||
701 | |||
702 | func (c *Checker) processPkg(graph *Graph, pkg *pkg) { | ||
703 | if pkg.Pkg.Path() == "unsafe" { | ||
704 | return | ||
705 | } | ||
706 | graph.entry(pkg) | ||
707 | } | ||
708 | |||
709 | func objNodeKeyFor(fset *token.FileSet, obj types.Object) objNodeKey { | ||
710 | var kind objType | ||
711 | switch obj.(type) { | ||
712 | case *types.PkgName: | ||
713 | kind = otPkgName | ||
714 | case *types.Const: | ||
715 | kind = otConst | ||
716 | case *types.TypeName: | ||
717 | kind = otTypeName | ||
718 | case *types.Var: | ||
719 | kind = otVar | ||
720 | case *types.Func: | ||
721 | kind = otFunc | ||
722 | case *types.Label: | ||
723 | kind = otLabel | ||
724 | case *types.Builtin: | ||
725 | kind = otBuiltin | ||
726 | case *types.Nil: | ||
727 | kind = otNil | ||
728 | default: | ||
729 | panic(fmt.Sprintf("unreachable: %T", obj)) | ||
730 | } | ||
731 | |||
732 | position := fset.PositionFor(obj.Pos(), false) | ||
733 | position.Column = 0 | ||
734 | position.Offset = 0 | ||
735 | return objNodeKey{ | ||
736 | position: position, | ||
737 | kind: kind, | ||
738 | name: obj.Name(), | ||
739 | } | ||
740 | } | ||
741 | |||
742 | type objType uint8 | ||
743 | |||
744 | const ( | ||
745 | otPkgName objType = iota | ||
746 | otConst | ||
747 | otTypeName | ||
748 | otVar | ||
749 | otFunc | ||
750 | otLabel | ||
751 | otBuiltin | ||
752 | otNil | ||
753 | ) | ||
754 | |||
755 | // An objNodeKey describes a types.Object node in the graph. | ||
756 | // | ||
757 | // Due to test variants we may end up with multiple instances of the | ||
758 | // same object, which is why we have to deduplicate based on their | ||
759 | // source position. And because export data lacks column information, | ||
760 | // we also have to incorporate the object's string representation in | ||
761 | // the key. | ||
762 | // | ||
763 | // Previously we used the object's full string representation | ||
764 | // (types.ObjectString), but that causes a significant amount of | ||
765 | // allocations. Currently we're using the object's type and name, in | ||
766 | // the hope that it is impossible for two objects to have the same | ||
767 | // type, name and file position. | ||
768 | type objNodeKey struct { | ||
769 | position token.Position | ||
770 | kind objType | ||
771 | name string | ||
772 | } | ||
773 | |||
774 | type Graph struct { | ||
775 | // accessed atomically | ||
776 | nodeOffset uint64 | ||
777 | |||
778 | // Safe for concurrent use | ||
779 | fset *token.FileSet | ||
780 | Root *Node | ||
781 | seenTypes typeutil.Map | ||
782 | Nodes sync.Map // map[interface{}]*Node | ||
783 | objNodes sync.Map // map[objNodeKey]*Node | ||
784 | |||
785 | // read-only | ||
786 | wholeProgram bool | ||
787 | |||
788 | // need synchronisation | ||
789 | mu sync.Mutex | ||
790 | TypeNodes typeutil.Map | ||
791 | } | ||
792 | |||
793 | type context struct { | ||
794 | g *Graph | ||
795 | pkg *pkg | ||
796 | seenFns map[string]struct{} | ||
797 | seenTypes *typeutil.Map | ||
798 | nodeCounter uint64 | ||
799 | |||
800 | // local cache for the map in Graph | ||
801 | typeNodes typeutil.Map | ||
802 | } | ||
803 | |||
804 | func NewGraph() *Graph { | ||
805 | g := &Graph{} | ||
806 | g.Root = g.newNode(&context{}, nil) | ||
807 | return g | ||
808 | } | ||
809 | |||
810 | func (g *Graph) color(root *Node) { | ||
811 | if root.seen { | ||
812 | return | ||
813 | } | ||
814 | root.seen = true | ||
815 | for _, e := range root.used { | ||
816 | g.color(e.node) | ||
817 | } | ||
818 | } | ||
819 | |||
820 | type ConstGroup struct { | ||
821 | // give the struct a size to get unique pointers | ||
822 | _ byte | ||
823 | } | ||
824 | |||
825 | func (ConstGroup) String() string { return "const group" } | ||
826 | |||
827 | type edge struct { | ||
828 | node *Node | ||
829 | kind edgeKind | ||
830 | } | ||
831 | |||
832 | type Node struct { | ||
833 | obj interface{} | ||
834 | id uint64 | ||
835 | |||
836 | mu sync.Mutex | ||
837 | used []edge | ||
838 | |||
839 | // set during final graph walk if node is reachable | ||
840 | seen bool | ||
841 | // a parent node (e.g. the struct type containing a field) is | ||
842 | // already unused, don't report children | ||
843 | quiet bool | ||
844 | } | ||
845 | |||
846 | func (g *Graph) nodeMaybe(obj types.Object) (*Node, bool) { | ||
847 | if node, ok := g.Nodes.Load(obj); ok { | ||
848 | return node.(*Node), true | ||
849 | } | ||
850 | return nil, false | ||
851 | } | ||
852 | |||
853 | func (g *Graph) node(ctx *context, obj interface{}) (node *Node, new bool) { | ||
854 | if t, ok := obj.(types.Type); ok { | ||
855 | if v := ctx.typeNodes.At(t); v != nil { | ||
856 | return v.(*Node), false | ||
857 | } | ||
858 | g.mu.Lock() | ||
859 | defer g.mu.Unlock() | ||
860 | |||
861 | if v := g.TypeNodes.At(t); v != nil { | ||
862 | return v.(*Node), false | ||
863 | } | ||
864 | node := g.newNode(ctx, t) | ||
865 | g.TypeNodes.Set(t, node) | ||
866 | ctx.typeNodes.Set(t, node) | ||
867 | return node, true | ||
868 | } | ||
869 | |||
870 | if node, ok := g.Nodes.Load(obj); ok { | ||
871 | return node.(*Node), false | ||
872 | } | ||
873 | |||
874 | if obj, ok := obj.(types.Object); ok { | ||
875 | key := objNodeKeyFor(g.fset, obj) | ||
876 | if o, ok := g.objNodes.Load(key); ok { | ||
877 | onode := o.(*Node) | ||
878 | return onode, false | ||
879 | } | ||
880 | |||
881 | node = g.newNode(ctx, obj) | ||
882 | g.Nodes.Store(obj, node) | ||
883 | g.objNodes.Store(key, node) | ||
884 | return node, true | ||
885 | } | ||
886 | |||
887 | node = g.newNode(ctx, obj) | ||
888 | g.Nodes.Store(obj, node) | ||
889 | return node, true | ||
890 | } | ||
891 | |||
892 | func (g *Graph) newNode(ctx *context, obj interface{}) *Node { | ||
893 | ctx.nodeCounter++ | ||
894 | return &Node{ | ||
895 | obj: obj, | ||
896 | id: ctx.nodeCounter, | ||
897 | } | ||
898 | } | ||
899 | |||
900 | func (n *Node) use(node *Node, kind edgeKind) { | ||
901 | n.mu.Lock() | ||
902 | defer n.mu.Unlock() | ||
903 | assert(node != nil) | ||
904 | n.used = append(n.used, edge{node: node, kind: kind}) | ||
905 | } | ||
906 | |||
907 | // isIrrelevant reports whether an object's presence in the graph is | ||
908 | // of any relevance. A lot of objects will never have outgoing edges, | ||
909 | // nor meaningful incoming ones. Examples are basic types and empty | ||
910 | // signatures, among many others. | ||
911 | // | ||
912 | // Dropping these objects should have no effect on correctness, but | ||
913 | // may improve performance. It also helps with debugging, as it | ||
914 | // greatly reduces the size of the graph. | ||
915 | func isIrrelevant(obj interface{}) bool { | ||
916 | if obj, ok := obj.(types.Object); ok { | ||
917 | switch obj := obj.(type) { | ||
918 | case *types.Var: | ||
919 | if obj.IsField() { | ||
920 | // We need to track package fields | ||
921 | return false | ||
922 | } | ||
923 | if obj.Pkg() != nil && obj.Parent() == obj.Pkg().Scope() { | ||
924 | // We need to track package-level variables | ||
925 | return false | ||
926 | } | ||
927 | return isIrrelevant(obj.Type()) | ||
928 | default: | ||
929 | return false | ||
930 | } | ||
931 | } | ||
932 | if T, ok := obj.(types.Type); ok { | ||
933 | switch T := T.(type) { | ||
934 | case *types.Array: | ||
935 | return isIrrelevant(T.Elem()) | ||
936 | case *types.Slice: | ||
937 | return isIrrelevant(T.Elem()) | ||
938 | case *types.Basic: | ||
939 | return true | ||
940 | case *types.Tuple: | ||
941 | for i := 0; i < T.Len(); i++ { | ||
942 | if !isIrrelevant(T.At(i).Type()) { | ||
943 | return false | ||
944 | } | ||
945 | } | ||
946 | return true | ||
947 | case *types.Signature: | ||
948 | if T.Recv() != nil { | ||
949 | return false | ||
950 | } | ||
951 | for i := 0; i < T.Params().Len(); i++ { | ||
952 | if !isIrrelevant(T.Params().At(i)) { | ||
953 | return false | ||
954 | } | ||
955 | } | ||
956 | for i := 0; i < T.Results().Len(); i++ { | ||
957 | if !isIrrelevant(T.Results().At(i)) { | ||
958 | return false | ||
959 | } | ||
960 | } | ||
961 | return true | ||
962 | case *types.Interface: | ||
963 | return T.NumMethods() == 0 && T.NumEmbeddeds() == 0 | ||
964 | case *types.Pointer: | ||
965 | return isIrrelevant(T.Elem()) | ||
966 | case *types.Map: | ||
967 | return isIrrelevant(T.Key()) && isIrrelevant(T.Elem()) | ||
968 | case *types.Struct: | ||
969 | return T.NumFields() == 0 | ||
970 | case *types.Chan: | ||
971 | return isIrrelevant(T.Elem()) | ||
972 | default: | ||
973 | return false | ||
974 | } | ||
975 | } | ||
976 | return false | ||
977 | } | ||
978 | |||
979 | func (ctx *context) see(obj interface{}) *Node { | ||
980 | if isIrrelevant(obj) { | ||
981 | return nil | ||
982 | } | ||
983 | |||
984 | assert(obj != nil) | ||
985 | // add new node to graph | ||
986 | node, _ := ctx.g.node(ctx, obj) | ||
987 | return node | ||
988 | } | ||
989 | |||
990 | func (ctx *context) use(used, by interface{}, kind edgeKind) { | ||
991 | if isIrrelevant(used) { | ||
992 | return | ||
993 | } | ||
994 | |||
995 | assert(used != nil) | ||
996 | if obj, ok := by.(types.Object); ok && obj.Pkg() != nil { | ||
997 | if !ctx.g.wholeProgram && obj.Pkg() != ctx.pkg.Pkg { | ||
998 | return | ||
999 | } | ||
1000 | } | ||
1001 | usedNode, new := ctx.g.node(ctx, used) | ||
1002 | assert(!new) | ||
1003 | if by == nil { | ||
1004 | ctx.g.Root.use(usedNode, kind) | ||
1005 | } else { | ||
1006 | byNode, new := ctx.g.node(ctx, by) | ||
1007 | assert(!new) | ||
1008 | byNode.use(usedNode, kind) | ||
1009 | } | ||
1010 | } | ||
1011 | |||
1012 | func (ctx *context) seeAndUse(used, by interface{}, kind edgeKind) *Node { | ||
1013 | node := ctx.see(used) | ||
1014 | ctx.use(used, by, kind) | ||
1015 | return node | ||
1016 | } | ||
1017 | |||
1018 | // trackExportedIdentifier reports whether obj should be considered | ||
1019 | // used due to being exported, checking various conditions that affect | ||
1020 | // the decision. | ||
1021 | func (g *Graph) trackExportedIdentifier(ctx *context, obj types.Object) bool { | ||
1022 | if !obj.Exported() { | ||
1023 | // object isn't exported, the question is moot | ||
1024 | return false | ||
1025 | } | ||
1026 | path := g.fset.Position(obj.Pos()).Filename | ||
1027 | if g.wholeProgram { | ||
1028 | // Example functions without "Output:" comments aren't being | ||
1029 | // run and thus don't show up in the graph. | ||
1030 | if strings.HasSuffix(path, "_test.go") && strings.HasPrefix(obj.Name(), "Example") { | ||
1031 | return true | ||
1032 | } | ||
1033 | // whole program mode tracks exported identifiers accurately | ||
1034 | return false | ||
1035 | } | ||
1036 | |||
1037 | if ctx.pkg.Pkg.Name() == "main" && !strings.HasSuffix(path, "_test.go") { | ||
1038 | // exported identifiers in package main can't be imported. | ||
1039 | // However, test functions can be called, and xtest packages | ||
1040 | // even have access to exported identifiers. | ||
1041 | return false | ||
1042 | } | ||
1043 | |||
1044 | if strings.HasSuffix(path, "_test.go") { | ||
1045 | if strings.HasPrefix(obj.Name(), "Test") || | ||
1046 | strings.HasPrefix(obj.Name(), "Benchmark") || | ||
1047 | strings.HasPrefix(obj.Name(), "Example") { | ||
1048 | return true | ||
1049 | } | ||
1050 | return false | ||
1051 | } | ||
1052 | |||
1053 | return true | ||
1054 | } | ||
1055 | |||
1056 | func (g *Graph) entry(pkg *pkg) { | ||
1057 | no := atomic.AddUint64(&g.nodeOffset, 1) | ||
1058 | ctx := &context{ | ||
1059 | g: g, | ||
1060 | pkg: pkg, | ||
1061 | nodeCounter: no * 1e9, | ||
1062 | seenFns: map[string]struct{}{}, | ||
1063 | } | ||
1064 | if g.wholeProgram { | ||
1065 | ctx.seenTypes = &g.seenTypes | ||
1066 | } else { | ||
1067 | ctx.seenTypes = &typeutil.Map{} | ||
1068 | } | ||
1069 | |||
1070 | scopes := map[*types.Scope]*ssa.Function{} | ||
1071 | for _, fn := range pkg.SrcFuncs { | ||
1072 | if fn.Object() != nil { | ||
1073 | scope := fn.Object().(*types.Func).Scope() | ||
1074 | scopes[scope] = fn | ||
1075 | } | ||
1076 | } | ||
1077 | |||
1078 | for _, f := range pkg.Files { | ||
1079 | for _, cg := range f.Comments { | ||
1080 | for _, c := range cg.List { | ||
1081 | if strings.HasPrefix(c.Text, "//go:linkname ") { | ||
1082 | // FIXME(dh): we're looking at all comments. The | ||
1083 | // compiler only looks at comments in the | ||
1084 | // left-most column. The intention probably is to | ||
1085 | // only look at top-level comments. | ||
1086 | |||
1087 | // (1.8) packages use symbols linked via go:linkname | ||
1088 | fields := strings.Fields(c.Text) | ||
1089 | if len(fields) == 3 { | ||
1090 | if m, ok := pkg.SSA.Members[fields[1]]; ok { | ||
1091 | var obj types.Object | ||
1092 | switch m := m.(type) { | ||
1093 | case *ssa.Global: | ||
1094 | obj = m.Object() | ||
1095 | case *ssa.Function: | ||
1096 | obj = m.Object() | ||
1097 | default: | ||
1098 | panic(fmt.Sprintf("unhandled type: %T", m)) | ||
1099 | } | ||
1100 | assert(obj != nil) | ||
1101 | ctx.seeAndUse(obj, nil, edgeLinkname) | ||
1102 | } | ||
1103 | } | ||
1104 | } | ||
1105 | } | ||
1106 | } | ||
1107 | } | ||
1108 | |||
1109 | surroundingFunc := func(obj types.Object) *ssa.Function { | ||
1110 | scope := obj.Parent() | ||
1111 | for scope != nil { | ||
1112 | if fn := scopes[scope]; fn != nil { | ||
1113 | return fn | ||
1114 | } | ||
1115 | scope = scope.Parent() | ||
1116 | } | ||
1117 | return nil | ||
1118 | } | ||
1119 | |||
1120 | // SSA form won't tell us about locally scoped types that aren't | ||
1121 | // being used. Walk the list of Defs to get all named types. | ||
1122 | // | ||
1123 | // SSA form also won't tell us about constants; use Defs and Uses | ||
1124 | // to determine which constants exist and which are being used. | ||
1125 | for _, obj := range pkg.TypesInfo.Defs { | ||
1126 | switch obj := obj.(type) { | ||
1127 | case *types.TypeName: | ||
1128 | // types are being handled by walking the AST | ||
1129 | case *types.Const: | ||
1130 | ctx.see(obj) | ||
1131 | fn := surroundingFunc(obj) | ||
1132 | if fn == nil && g.trackExportedIdentifier(ctx, obj) { | ||
1133 | // (1.4) packages use exported constants (unless in package main) | ||
1134 | ctx.use(obj, nil, edgeExportedConstant) | ||
1135 | } | ||
1136 | g.typ(ctx, obj.Type(), nil) | ||
1137 | ctx.seeAndUse(obj.Type(), obj, edgeType) | ||
1138 | } | ||
1139 | } | ||
1140 | |||
1141 | // Find constants being used inside functions, find sinks in tests | ||
1142 | for _, fn := range pkg.SrcFuncs { | ||
1143 | if fn.Object() != nil { | ||
1144 | ctx.see(fn.Object()) | ||
1145 | } | ||
1146 | node := fn.Syntax() | ||
1147 | if node == nil { | ||
1148 | continue | ||
1149 | } | ||
1150 | ast.Inspect(node, func(node ast.Node) bool { | ||
1151 | switch node := node.(type) { | ||
1152 | case *ast.Ident: | ||
1153 | obj, ok := pkg.TypesInfo.Uses[node] | ||
1154 | if !ok { | ||
1155 | return true | ||
1156 | } | ||
1157 | switch obj := obj.(type) { | ||
1158 | case *types.Const: | ||
1159 | ctx.seeAndUse(obj, owningObject(fn), edgeUsedConstant) | ||
1160 | } | ||
1161 | case *ast.AssignStmt: | ||
1162 | for _, expr := range node.Lhs { | ||
1163 | ident, ok := expr.(*ast.Ident) | ||
1164 | if !ok { | ||
1165 | continue | ||
1166 | } | ||
1167 | obj := pkg.TypesInfo.ObjectOf(ident) | ||
1168 | if obj == nil { | ||
1169 | continue | ||
1170 | } | ||
1171 | path := g.fset.File(obj.Pos()).Name() | ||
1172 | if strings.HasSuffix(path, "_test.go") { | ||
1173 | if obj.Parent() != nil && obj.Parent().Parent() != nil && obj.Parent().Parent().Parent() == nil { | ||
1174 | // object's scope is the package, whose | ||
1175 | // parent is the file, whose parent is nil | ||
1176 | |||
1177 | // (4.9) functions use package-level variables they assign to iff in tests (sinks for benchmarks) | ||
1178 | // (9.7) variable _reads_ use variables, writes do not, except in tests | ||
1179 | ctx.seeAndUse(obj, owningObject(fn), edgeTestSink) | ||
1180 | } | ||
1181 | } | ||
1182 | } | ||
1183 | } | ||
1184 | |||
1185 | return true | ||
1186 | }) | ||
1187 | } | ||
1188 | // Find constants being used in non-function contexts | ||
1189 | for _, obj := range pkg.TypesInfo.Uses { | ||
1190 | _, ok := obj.(*types.Const) | ||
1191 | if !ok { | ||
1192 | continue | ||
1193 | } | ||
1194 | ctx.seeAndUse(obj, nil, edgeUsedConstant) | ||
1195 | } | ||
1196 | |||
1197 | var fns []*types.Func | ||
1198 | var fn *types.Func | ||
1199 | var stack []ast.Node | ||
1200 | for _, f := range pkg.Files { | ||
1201 | ast.Inspect(f, func(n ast.Node) bool { | ||
1202 | if n == nil { | ||
1203 | pop := stack[len(stack)-1] | ||
1204 | stack = stack[:len(stack)-1] | ||
1205 | if _, ok := pop.(*ast.FuncDecl); ok { | ||
1206 | fns = fns[:len(fns)-1] | ||
1207 | if len(fns) == 0 { | ||
1208 | fn = nil | ||
1209 | } else { | ||
1210 | fn = fns[len(fns)-1] | ||
1211 | } | ||
1212 | } | ||
1213 | return true | ||
1214 | } | ||
1215 | stack = append(stack, n) | ||
1216 | switch n := n.(type) { | ||
1217 | case *ast.FuncDecl: | ||
1218 | fn = pkg.TypesInfo.ObjectOf(n.Name).(*types.Func) | ||
1219 | fns = append(fns, fn) | ||
1220 | ctx.see(fn) | ||
1221 | case *ast.GenDecl: | ||
1222 | switch n.Tok { | ||
1223 | case token.CONST: | ||
1224 | groups := lintdsl.GroupSpecs(pkg.Fset, n.Specs) | ||
1225 | for _, specs := range groups { | ||
1226 | if len(specs) > 1 { | ||
1227 | cg := &ConstGroup{} | ||
1228 | ctx.see(cg) | ||
1229 | for _, spec := range specs { | ||
1230 | for _, name := range spec.(*ast.ValueSpec).Names { | ||
1231 | obj := pkg.TypesInfo.ObjectOf(name) | ||
1232 | // (10.1) const groups | ||
1233 | ctx.seeAndUse(obj, cg, edgeConstGroup) | ||
1234 | ctx.use(cg, obj, edgeConstGroup) | ||
1235 | } | ||
1236 | } | ||
1237 | } | ||
1238 | } | ||
1239 | case token.VAR: | ||
1240 | for _, spec := range n.Specs { | ||
1241 | v := spec.(*ast.ValueSpec) | ||
1242 | for _, name := range v.Names { | ||
1243 | T := pkg.TypesInfo.TypeOf(name) | ||
1244 | if fn != nil { | ||
1245 | ctx.seeAndUse(T, fn, edgeVarDecl) | ||
1246 | } else { | ||
1247 | // TODO(dh): we likely want to make | ||
1248 | // the type used by the variable, not | ||
1249 | // the package containing the | ||
1250 | // variable. But then we have to take | ||
1251 | // special care of blank identifiers. | ||
1252 | ctx.seeAndUse(T, nil, edgeVarDecl) | ||
1253 | } | ||
1254 | g.typ(ctx, T, nil) | ||
1255 | } | ||
1256 | } | ||
1257 | case token.TYPE: | ||
1258 | for _, spec := range n.Specs { | ||
1259 | // go/types doesn't provide a way to go from a | ||
1260 | // types.Named to the named type it was based on | ||
1261 | // (the t1 in type t2 t1). Therefore we walk the | ||
1262 | // AST and process GenDecls. | ||
1263 | // | ||
1264 | // (2.2) named types use the type they're based on | ||
1265 | v := spec.(*ast.TypeSpec) | ||
1266 | T := pkg.TypesInfo.TypeOf(v.Type) | ||
1267 | obj := pkg.TypesInfo.ObjectOf(v.Name) | ||
1268 | ctx.see(obj) | ||
1269 | ctx.see(T) | ||
1270 | ctx.use(T, obj, edgeType) | ||
1271 | g.typ(ctx, obj.Type(), nil) | ||
1272 | g.typ(ctx, T, nil) | ||
1273 | |||
1274 | if v.Assign != 0 { | ||
1275 | aliasFor := obj.(*types.TypeName).Type() | ||
1276 | // (2.3) named types use all their aliases. we can't easily track uses of aliases | ||
1277 | if isIrrelevant(aliasFor) { | ||
1278 | // We do not track the type this is an | ||
1279 | // alias for (for example builtins), so | ||
1280 | // just mark the alias used. | ||
1281 | // | ||
1282 | // FIXME(dh): what about aliases declared inside functions? | ||
1283 | ctx.use(obj, nil, edgeAlias) | ||
1284 | } else { | ||
1285 | ctx.see(aliasFor) | ||
1286 | ctx.seeAndUse(obj, aliasFor, edgeAlias) | ||
1287 | } | ||
1288 | } | ||
1289 | } | ||
1290 | } | ||
1291 | } | ||
1292 | return true | ||
1293 | }) | ||
1294 | } | ||
1295 | |||
1296 | for _, m := range pkg.SSA.Members { | ||
1297 | switch m := m.(type) { | ||
1298 | case *ssa.NamedConst: | ||
1299 | // nothing to do, we collect all constants from Defs | ||
1300 | case *ssa.Global: | ||
1301 | if m.Object() != nil { | ||
1302 | ctx.see(m.Object()) | ||
1303 | if g.trackExportedIdentifier(ctx, m.Object()) { | ||
1304 | // (1.3) packages use exported variables (unless in package main) | ||
1305 | ctx.use(m.Object(), nil, edgeExportedVariable) | ||
1306 | } | ||
1307 | } | ||
1308 | case *ssa.Function: | ||
1309 | mObj := owningObject(m) | ||
1310 | if mObj != nil { | ||
1311 | ctx.see(mObj) | ||
1312 | } | ||
1313 | //lint:ignore SA9003 handled implicitly | ||
1314 | if m.Name() == "init" { | ||
1315 | // (1.5) packages use init functions | ||
1316 | // | ||
1317 | // This is handled implicitly. The generated init | ||
1318 | // function has no object, thus everything in it will | ||
1319 | // be owned by the package. | ||
1320 | } | ||
1321 | // This branch catches top-level functions, not methods. | ||
1322 | if m.Object() != nil && g.trackExportedIdentifier(ctx, m.Object()) { | ||
1323 | // (1.2) packages use exported functions (unless in package main) | ||
1324 | ctx.use(mObj, nil, edgeExportedFunction) | ||
1325 | } | ||
1326 | if m.Name() == "main" && pkg.Pkg.Name() == "main" { | ||
1327 | // (1.7) packages use the main function iff in the main package | ||
1328 | ctx.use(mObj, nil, edgeMainFunction) | ||
1329 | } | ||
1330 | if pkg.Pkg.Path() == "runtime" && runtimeFuncs[m.Name()] { | ||
1331 | // (9.8) runtime functions that may be called from user code via the compiler | ||
1332 | ctx.use(mObj, nil, edgeRuntimeFunction) | ||
1333 | } | ||
1334 | if m.Syntax() != nil { | ||
1335 | doc := m.Syntax().(*ast.FuncDecl).Doc | ||
1336 | if doc != nil { | ||
1337 | for _, cmt := range doc.List { | ||
1338 | if strings.HasPrefix(cmt.Text, "//go:cgo_export_") { | ||
1339 | // (1.6) packages use functions exported to cgo | ||
1340 | ctx.use(mObj, nil, edgeCgoExported) | ||
1341 | } | ||
1342 | } | ||
1343 | } | ||
1344 | } | ||
1345 | g.function(ctx, m) | ||
1346 | case *ssa.Type: | ||
1347 | if m.Object() != nil { | ||
1348 | ctx.see(m.Object()) | ||
1349 | if g.trackExportedIdentifier(ctx, m.Object()) { | ||
1350 | // (1.1) packages use exported named types (unless in package main) | ||
1351 | ctx.use(m.Object(), nil, edgeExportedType) | ||
1352 | } | ||
1353 | } | ||
1354 | g.typ(ctx, m.Type(), nil) | ||
1355 | default: | ||
1356 | panic(fmt.Sprintf("unreachable: %T", m)) | ||
1357 | } | ||
1358 | } | ||
1359 | |||
1360 | if !g.wholeProgram { | ||
1361 | // When not in whole program mode we reset seenTypes after each package, | ||
1362 | // which means g.seenTypes only contains types of | ||
1363 | // interest to us. In whole program mode, we're better off | ||
1364 | // processing all interfaces at once, globally, both for | ||
1365 | // performance reasons and because in whole program mode we | ||
1366 | // actually care about all interfaces, not just the subset | ||
1367 | // that has unexported methods. | ||
1368 | |||
1369 | var ifaces []*types.Interface | ||
1370 | var notIfaces []types.Type | ||
1371 | |||
1372 | ctx.seenTypes.Iterate(func(t types.Type, _ interface{}) { | ||
1373 | switch t := t.(type) { | ||
1374 | case *types.Interface: | ||
1375 | // OPT(dh): (8.1) we only need interfaces that have unexported methods | ||
1376 | ifaces = append(ifaces, t) | ||
1377 | default: | ||
1378 | if _, ok := t.Underlying().(*types.Interface); !ok { | ||
1379 | notIfaces = append(notIfaces, t) | ||
1380 | } | ||
1381 | } | ||
1382 | }) | ||
1383 | |||
1384 | // (8.0) handle interfaces | ||
1385 | for _, t := range notIfaces { | ||
1386 | ms := pkg.SSA.Prog.MethodSets.MethodSet(t) | ||
1387 | for _, iface := range ifaces { | ||
1388 | if sels, ok := g.implements(t, iface, ms); ok { | ||
1389 | for _, sel := range sels { | ||
1390 | g.useMethod(ctx, t, sel, t, edgeImplements) | ||
1391 | } | ||
1392 | } | ||
1393 | } | ||
1394 | } | ||
1395 | } | ||
1396 | } | ||
1397 | |||
1398 | func (g *Graph) useMethod(ctx *context, t types.Type, sel *types.Selection, by interface{}, kind edgeKind) { | ||
1399 | obj := sel.Obj() | ||
1400 | path := sel.Index() | ||
1401 | assert(obj != nil) | ||
1402 | if len(path) > 1 { | ||
1403 | base := lintdsl.Dereference(t).Underlying().(*types.Struct) | ||
1404 | for _, idx := range path[:len(path)-1] { | ||
1405 | next := base.Field(idx) | ||
1406 | // (6.3) structs use embedded fields that help implement interfaces | ||
1407 | ctx.see(base) | ||
1408 | ctx.seeAndUse(next, base, edgeProvidesMethod) | ||
1409 | base, _ = lintdsl.Dereference(next.Type()).Underlying().(*types.Struct) | ||
1410 | } | ||
1411 | } | ||
1412 | ctx.seeAndUse(obj, by, kind) | ||
1413 | } | ||
1414 | |||
1415 | func owningObject(fn *ssa.Function) types.Object { | ||
1416 | if fn.Object() != nil { | ||
1417 | return fn.Object() | ||
1418 | } | ||
1419 | if fn.Parent() != nil { | ||
1420 | return owningObject(fn.Parent()) | ||
1421 | } | ||
1422 | return nil | ||
1423 | } | ||
1424 | |||
1425 | func (g *Graph) function(ctx *context, fn *ssa.Function) { | ||
1426 | if fn.Package() != nil && fn.Package() != ctx.pkg.SSA { | ||
1427 | return | ||
1428 | } | ||
1429 | |||
1430 | name := fn.RelString(nil) | ||
1431 | if _, ok := ctx.seenFns[name]; ok { | ||
1432 | return | ||
1433 | } | ||
1434 | ctx.seenFns[name] = struct{}{} | ||
1435 | |||
1436 | // (4.1) functions use all their arguments, return parameters and receivers | ||
1437 | g.signature(ctx, fn.Signature, owningObject(fn)) | ||
1438 | g.instructions(ctx, fn) | ||
1439 | for _, anon := range fn.AnonFuncs { | ||
1440 | // (4.2) functions use anonymous functions defined beneath them | ||
1441 | // | ||
1442 | // This fact is expressed implicitly. Anonymous functions have | ||
1443 | // no types.Object, so their owner is the surrounding | ||
1444 | // function. | ||
1445 | g.function(ctx, anon) | ||
1446 | } | ||
1447 | } | ||
1448 | |||
1449 | func (g *Graph) typ(ctx *context, t types.Type, parent types.Type) { | ||
1450 | if g.wholeProgram { | ||
1451 | g.mu.Lock() | ||
1452 | } | ||
1453 | if ctx.seenTypes.At(t) != nil { | ||
1454 | if g.wholeProgram { | ||
1455 | g.mu.Unlock() | ||
1456 | } | ||
1457 | return | ||
1458 | } | ||
1459 | if g.wholeProgram { | ||
1460 | g.mu.Unlock() | ||
1461 | } | ||
1462 | if t, ok := t.(*types.Named); ok && t.Obj().Pkg() != nil { | ||
1463 | if t.Obj().Pkg() != ctx.pkg.Pkg { | ||
1464 | return | ||
1465 | } | ||
1466 | } | ||
1467 | |||
1468 | if g.wholeProgram { | ||
1469 | g.mu.Lock() | ||
1470 | } | ||
1471 | ctx.seenTypes.Set(t, struct{}{}) | ||
1472 | if g.wholeProgram { | ||
1473 | g.mu.Unlock() | ||
1474 | } | ||
1475 | if isIrrelevant(t) { | ||
1476 | return | ||
1477 | } | ||
1478 | |||
1479 | ctx.see(t) | ||
1480 | switch t := t.(type) { | ||
1481 | case *types.Struct: | ||
1482 | for i := 0; i < t.NumFields(); i++ { | ||
1483 | ctx.see(t.Field(i)) | ||
1484 | if t.Field(i).Exported() { | ||
1485 | // (6.2) structs use exported fields | ||
1486 | ctx.use(t.Field(i), t, edgeExportedField) | ||
1487 | } else if t.Field(i).Name() == "_" { | ||
1488 | ctx.use(t.Field(i), t, edgeBlankField) | ||
1489 | } else if isNoCopyType(t.Field(i).Type()) { | ||
1490 | // (6.1) structs use fields of type NoCopy sentinel | ||
1491 | ctx.use(t.Field(i), t, edgeNoCopySentinel) | ||
1492 | } else if parent == nil { | ||
1493 | // (11.1) anonymous struct types use all their fields. | ||
1494 | ctx.use(t.Field(i), t, edgeAnonymousStruct) | ||
1495 | } | ||
1496 | if t.Field(i).Anonymous() { | ||
1497 | // (e3) exported identifiers aren't automatically used. | ||
1498 | if !g.wholeProgram { | ||
1499 | // does the embedded field contribute exported methods to the method set? | ||
1500 | T := t.Field(i).Type() | ||
1501 | if _, ok := T.Underlying().(*types.Pointer); !ok { | ||
1502 | // An embedded field is addressable, so check | ||
1503 | // the pointer type to get the full method set | ||
1504 | T = types.NewPointer(T) | ||
1505 | } | ||
1506 | ms := ctx.pkg.SSA.Prog.MethodSets.MethodSet(T) | ||
1507 | for j := 0; j < ms.Len(); j++ { | ||
1508 | if ms.At(j).Obj().Exported() { | ||
1509 | // (6.4) structs use embedded fields that have exported methods (recursively) | ||
1510 | ctx.use(t.Field(i), t, edgeExtendsExportedMethodSet) | ||
1511 | break | ||
1512 | } | ||
1513 | } | ||
1514 | } | ||
1515 | |||
1516 | seen := map[*types.Struct]struct{}{} | ||
1517 | var hasExportedField func(t types.Type) bool | ||
1518 | hasExportedField = func(T types.Type) bool { | ||
1519 | t, ok := lintdsl.Dereference(T).Underlying().(*types.Struct) | ||
1520 | if !ok { | ||
1521 | return false | ||
1522 | } | ||
1523 | if _, ok := seen[t]; ok { | ||
1524 | return false | ||
1525 | } | ||
1526 | seen[t] = struct{}{} | ||
1527 | for i := 0; i < t.NumFields(); i++ { | ||
1528 | field := t.Field(i) | ||
1529 | if field.Exported() { | ||
1530 | return true | ||
1531 | } | ||
1532 | if field.Embedded() && hasExportedField(field.Type()) { | ||
1533 | return true | ||
1534 | } | ||
1535 | } | ||
1536 | return false | ||
1537 | } | ||
1538 | // does the embedded field contribute exported fields? | ||
1539 | if hasExportedField(t.Field(i).Type()) { | ||
1540 | // (6.5) structs use embedded structs that have exported fields (recursively) | ||
1541 | ctx.use(t.Field(i), t, edgeExtendsExportedFields) | ||
1542 | } | ||
1543 | |||
1544 | } | ||
1545 | g.variable(ctx, t.Field(i)) | ||
1546 | } | ||
1547 | case *types.Basic: | ||
1548 | // Nothing to do | ||
1549 | case *types.Named: | ||
1550 | // (9.3) types use their underlying and element types | ||
1551 | ctx.seeAndUse(t.Underlying(), t, edgeUnderlyingType) | ||
1552 | ctx.seeAndUse(t.Obj(), t, edgeTypeName) | ||
1553 | ctx.seeAndUse(t, t.Obj(), edgeNamedType) | ||
1554 | |||
1555 | // (2.4) named types use the pointer type | ||
1556 | if _, ok := t.Underlying().(*types.Interface); !ok && t.NumMethods() > 0 { | ||
1557 | ctx.seeAndUse(types.NewPointer(t), t, edgePointerType) | ||
1558 | } | ||
1559 | |||
1560 | for i := 0; i < t.NumMethods(); i++ { | ||
1561 | ctx.see(t.Method(i)) | ||
1562 | // don't use trackExportedIdentifier here, we care about | ||
1563 | // all exported methods, even in package main or in tests. | ||
1564 | if t.Method(i).Exported() && !g.wholeProgram { | ||
1565 | // (2.1) named types use exported methods | ||
1566 | ctx.use(t.Method(i), t, edgeExportedMethod) | ||
1567 | } | ||
1568 | g.function(ctx, ctx.pkg.SSA.Prog.FuncValue(t.Method(i))) | ||
1569 | } | ||
1570 | |||
1571 | g.typ(ctx, t.Underlying(), t) | ||
1572 | case *types.Slice: | ||
1573 | // (9.3) types use their underlying and element types | ||
1574 | ctx.seeAndUse(t.Elem(), t, edgeElementType) | ||
1575 | g.typ(ctx, t.Elem(), nil) | ||
1576 | case *types.Map: | ||
1577 | // (9.3) types use their underlying and element types | ||
1578 | ctx.seeAndUse(t.Elem(), t, edgeElementType) | ||
1579 | // (9.3) types use their underlying and element types | ||
1580 | ctx.seeAndUse(t.Key(), t, edgeKeyType) | ||
1581 | g.typ(ctx, t.Elem(), nil) | ||
1582 | g.typ(ctx, t.Key(), nil) | ||
1583 | case *types.Signature: | ||
1584 | g.signature(ctx, t, nil) | ||
1585 | case *types.Interface: | ||
1586 | for i := 0; i < t.NumMethods(); i++ { | ||
1587 | m := t.Method(i) | ||
1588 | // (8.3) All interface methods are marked as used | ||
1589 | ctx.seeAndUse(m, t, edgeInterfaceMethod) | ||
1590 | ctx.seeAndUse(m.Type().(*types.Signature), m, edgeSignature) | ||
1591 | g.signature(ctx, m.Type().(*types.Signature), nil) | ||
1592 | } | ||
1593 | for i := 0; i < t.NumEmbeddeds(); i++ { | ||
1594 | tt := t.EmbeddedType(i) | ||
1595 | // (8.4) All embedded interfaces are marked as used | ||
1596 | ctx.seeAndUse(tt, t, edgeEmbeddedInterface) | ||
1597 | } | ||
1598 | case *types.Array: | ||
1599 | // (9.3) types use their underlying and element types | ||
1600 | ctx.seeAndUse(t.Elem(), t, edgeElementType) | ||
1601 | g.typ(ctx, t.Elem(), nil) | ||
1602 | case *types.Pointer: | ||
1603 | // (9.3) types use their underlying and element types | ||
1604 | ctx.seeAndUse(t.Elem(), t, edgeElementType) | ||
1605 | g.typ(ctx, t.Elem(), nil) | ||
1606 | case *types.Chan: | ||
1607 | // (9.3) types use their underlying and element types | ||
1608 | ctx.seeAndUse(t.Elem(), t, edgeElementType) | ||
1609 | g.typ(ctx, t.Elem(), nil) | ||
1610 | case *types.Tuple: | ||
1611 | for i := 0; i < t.Len(); i++ { | ||
1612 | // (9.3) types use their underlying and element types | ||
1613 | ctx.seeAndUse(t.At(i).Type(), t, edgeTupleElement|edgeType) | ||
1614 | g.typ(ctx, t.At(i).Type(), nil) | ||
1615 | } | ||
1616 | default: | ||
1617 | panic(fmt.Sprintf("unreachable: %T", t)) | ||
1618 | } | ||
1619 | } | ||
1620 | |||
1621 | func (g *Graph) variable(ctx *context, v *types.Var) { | ||
1622 | // (9.2) variables use their types | ||
1623 | ctx.seeAndUse(v.Type(), v, edgeType) | ||
1624 | g.typ(ctx, v.Type(), nil) | ||
1625 | } | ||
1626 | |||
1627 | func (g *Graph) signature(ctx *context, sig *types.Signature, fn types.Object) { | ||
1628 | var user interface{} = fn | ||
1629 | if fn == nil { | ||
1630 | user = sig | ||
1631 | ctx.see(sig) | ||
1632 | } | ||
1633 | if sig.Recv() != nil { | ||
1634 | ctx.seeAndUse(sig.Recv().Type(), user, edgeReceiver|edgeType) | ||
1635 | g.typ(ctx, sig.Recv().Type(), nil) | ||
1636 | } | ||
1637 | for i := 0; i < sig.Params().Len(); i++ { | ||
1638 | param := sig.Params().At(i) | ||
1639 | ctx.seeAndUse(param.Type(), user, edgeFunctionArgument|edgeType) | ||
1640 | g.typ(ctx, param.Type(), nil) | ||
1641 | } | ||
1642 | for i := 0; i < sig.Results().Len(); i++ { | ||
1643 | param := sig.Results().At(i) | ||
1644 | ctx.seeAndUse(param.Type(), user, edgeFunctionResult|edgeType) | ||
1645 | g.typ(ctx, param.Type(), nil) | ||
1646 | } | ||
1647 | } | ||
1648 | |||
1649 | func (g *Graph) instructions(ctx *context, fn *ssa.Function) { | ||
1650 | fnObj := owningObject(fn) | ||
1651 | for _, b := range fn.Blocks { | ||
1652 | for _, instr := range b.Instrs { | ||
1653 | ops := instr.Operands(nil) | ||
1654 | switch instr.(type) { | ||
1655 | case *ssa.Store: | ||
1656 | // (9.7) variable _reads_ use variables, writes do not | ||
1657 | ops = ops[1:] | ||
1658 | case *ssa.DebugRef: | ||
1659 | ops = nil | ||
1660 | } | ||
1661 | for _, arg := range ops { | ||
1662 | walkPhi(*arg, func(v ssa.Value) { | ||
1663 | switch v := v.(type) { | ||
1664 | case *ssa.Function: | ||
1665 | // (4.3) functions use closures and bound methods. | ||
1666 | // (4.5) functions use functions they call | ||
1667 | // (9.5) instructions use their operands | ||
1668 | // (4.4) functions use functions they return. we assume that someone else will call the returned function | ||
1669 | if owningObject(v) != nil { | ||
1670 | ctx.seeAndUse(owningObject(v), fnObj, edgeInstructionOperand) | ||
1671 | } | ||
1672 | g.function(ctx, v) | ||
1673 | case *ssa.Const: | ||
1674 | // (9.6) instructions use their operands' types | ||
1675 | ctx.seeAndUse(v.Type(), fnObj, edgeType) | ||
1676 | g.typ(ctx, v.Type(), nil) | ||
1677 | case *ssa.Global: | ||
1678 | if v.Object() != nil { | ||
1679 | // (9.5) instructions use their operands | ||
1680 | ctx.seeAndUse(v.Object(), fnObj, edgeInstructionOperand) | ||
1681 | } | ||
1682 | } | ||
1683 | }) | ||
1684 | } | ||
1685 | if v, ok := instr.(ssa.Value); ok { | ||
1686 | if _, ok := v.(*ssa.Range); !ok { | ||
1687 | // See https://github.com/golang/go/issues/19670 | ||
1688 | |||
1689 | // (4.8) instructions use their types | ||
1690 | // (9.4) conversions use the type they convert to | ||
1691 | ctx.seeAndUse(v.Type(), fnObj, edgeType) | ||
1692 | g.typ(ctx, v.Type(), nil) | ||
1693 | } | ||
1694 | } | ||
1695 | switch instr := instr.(type) { | ||
1696 | case *ssa.Field: | ||
1697 | st := instr.X.Type().Underlying().(*types.Struct) | ||
1698 | field := st.Field(instr.Field) | ||
1699 | // (4.7) functions use fields they access | ||
1700 | ctx.seeAndUse(field, fnObj, edgeFieldAccess) | ||
1701 | case *ssa.FieldAddr: | ||
1702 | st := lintdsl.Dereference(instr.X.Type()).Underlying().(*types.Struct) | ||
1703 | field := st.Field(instr.Field) | ||
1704 | // (4.7) functions use fields they access | ||
1705 | ctx.seeAndUse(field, fnObj, edgeFieldAccess) | ||
1706 | case *ssa.Store: | ||
1707 | // nothing to do, handled generically by operands | ||
1708 | case *ssa.Call: | ||
1709 | c := instr.Common() | ||
1710 | if !c.IsInvoke() { | ||
1711 | // handled generically as an instruction operand | ||
1712 | |||
1713 | if g.wholeProgram { | ||
1714 | // (e3) special case known reflection-based method callers | ||
1715 | switch lintdsl.CallName(c) { | ||
1716 | case "net/rpc.Register", "net/rpc.RegisterName", "(*net/rpc.Server).Register", "(*net/rpc.Server).RegisterName": | ||
1717 | var arg ssa.Value | ||
1718 | switch lintdsl.CallName(c) { | ||
1719 | case "net/rpc.Register": | ||
1720 | arg = c.Args[0] | ||
1721 | case "net/rpc.RegisterName": | ||
1722 | arg = c.Args[1] | ||
1723 | case "(*net/rpc.Server).Register": | ||
1724 | arg = c.Args[1] | ||
1725 | case "(*net/rpc.Server).RegisterName": | ||
1726 | arg = c.Args[2] | ||
1727 | } | ||
1728 | walkPhi(arg, func(v ssa.Value) { | ||
1729 | if v, ok := v.(*ssa.MakeInterface); ok { | ||
1730 | walkPhi(v.X, func(vv ssa.Value) { | ||
1731 | ms := ctx.pkg.SSA.Prog.MethodSets.MethodSet(vv.Type()) | ||
1732 | for i := 0; i < ms.Len(); i++ { | ||
1733 | if ms.At(i).Obj().Exported() { | ||
1734 | g.useMethod(ctx, vv.Type(), ms.At(i), fnObj, edgeNetRPCRegister) | ||
1735 | } | ||
1736 | } | ||
1737 | }) | ||
1738 | } | ||
1739 | }) | ||
1740 | } | ||
1741 | } | ||
1742 | } else { | ||
1743 | // (4.5) functions use functions/interface methods they call | ||
1744 | ctx.seeAndUse(c.Method, fnObj, edgeInterfaceCall) | ||
1745 | } | ||
1746 | case *ssa.Return: | ||
1747 | // nothing to do, handled generically by operands | ||
1748 | case *ssa.ChangeType: | ||
1749 | // conversion type handled generically | ||
1750 | |||
1751 | s1, ok1 := lintdsl.Dereference(instr.Type()).Underlying().(*types.Struct) | ||
1752 | s2, ok2 := lintdsl.Dereference(instr.X.Type()).Underlying().(*types.Struct) | ||
1753 | if ok1 && ok2 { | ||
1754 | // Converting between two structs. The fields are | ||
1755 | // relevant for the conversion, but only if the | ||
1756 | // fields are also used outside of the conversion. | ||
1757 | // Mark fields as used by each other. | ||
1758 | |||
1759 | assert(s1.NumFields() == s2.NumFields()) | ||
1760 | for i := 0; i < s1.NumFields(); i++ { | ||
1761 | ctx.see(s1.Field(i)) | ||
1762 | ctx.see(s2.Field(i)) | ||
1763 | // (5.1) when converting between two equivalent structs, the fields in | ||
1764 | // either struct use each other. the fields are relevant for the | ||
1765 | // conversion, but only if the fields are also accessed outside the | ||
1766 | // conversion. | ||
1767 | ctx.seeAndUse(s1.Field(i), s2.Field(i), edgeStructConversion) | ||
1768 | ctx.seeAndUse(s2.Field(i), s1.Field(i), edgeStructConversion) | ||
1769 | } | ||
1770 | } | ||
1771 | case *ssa.MakeInterface: | ||
1772 | // nothing to do, handled generically by operands | ||
1773 | case *ssa.Slice: | ||
1774 | // nothing to do, handled generically by operands | ||
1775 | case *ssa.RunDefers: | ||
1776 | // nothing to do, the deferred functions are already marked use by defering them. | ||
1777 | case *ssa.Convert: | ||
1778 | // to unsafe.Pointer | ||
1779 | if typ, ok := instr.Type().(*types.Basic); ok && typ.Kind() == types.UnsafePointer { | ||
1780 | if ptr, ok := instr.X.Type().Underlying().(*types.Pointer); ok { | ||
1781 | if st, ok := ptr.Elem().Underlying().(*types.Struct); ok { | ||
1782 | for i := 0; i < st.NumFields(); i++ { | ||
1783 | // (5.2) when converting to or from unsafe.Pointer, mark all fields as used. | ||
1784 | ctx.seeAndUse(st.Field(i), fnObj, edgeUnsafeConversion) | ||
1785 | } | ||
1786 | } | ||
1787 | } | ||
1788 | } | ||
1789 | // from unsafe.Pointer | ||
1790 | if typ, ok := instr.X.Type().(*types.Basic); ok && typ.Kind() == types.UnsafePointer { | ||
1791 | if ptr, ok := instr.Type().Underlying().(*types.Pointer); ok { | ||
1792 | if st, ok := ptr.Elem().Underlying().(*types.Struct); ok { | ||
1793 | for i := 0; i < st.NumFields(); i++ { | ||
1794 | // (5.2) when converting to or from unsafe.Pointer, mark all fields as used. | ||
1795 | ctx.seeAndUse(st.Field(i), fnObj, edgeUnsafeConversion) | ||
1796 | } | ||
1797 | } | ||
1798 | } | ||
1799 | } | ||
1800 | case *ssa.TypeAssert: | ||
1801 | // nothing to do, handled generically by instruction | ||
1802 | // type (possibly a tuple, which contains the asserted | ||
1803 | // to type). redundantly handled by the type of | ||
1804 | // ssa.Extract, too | ||
1805 | case *ssa.MakeClosure: | ||
1806 | // nothing to do, handled generically by operands | ||
1807 | case *ssa.Alloc: | ||
1808 | // nothing to do | ||
1809 | case *ssa.UnOp: | ||
1810 | // nothing to do | ||
1811 | case *ssa.BinOp: | ||
1812 | // nothing to do | ||
1813 | case *ssa.If: | ||
1814 | // nothing to do | ||
1815 | case *ssa.Jump: | ||
1816 | // nothing to do | ||
1817 | case *ssa.IndexAddr: | ||
1818 | // nothing to do | ||
1819 | case *ssa.Extract: | ||
1820 | // nothing to do | ||
1821 | case *ssa.Panic: | ||
1822 | // nothing to do | ||
1823 | case *ssa.DebugRef: | ||
1824 | // nothing to do | ||
1825 | case *ssa.BlankStore: | ||
1826 | // nothing to do | ||
1827 | case *ssa.Phi: | ||
1828 | // nothing to do | ||
1829 | case *ssa.MakeMap: | ||
1830 | // nothing to do | ||
1831 | case *ssa.MapUpdate: | ||
1832 | // nothing to do | ||
1833 | case *ssa.Lookup: | ||
1834 | // nothing to do | ||
1835 | case *ssa.MakeSlice: | ||
1836 | // nothing to do | ||
1837 | case *ssa.Send: | ||
1838 | // nothing to do | ||
1839 | case *ssa.MakeChan: | ||
1840 | // nothing to do | ||
1841 | case *ssa.Range: | ||
1842 | // nothing to do | ||
1843 | case *ssa.Next: | ||
1844 | // nothing to do | ||
1845 | case *ssa.Index: | ||
1846 | // nothing to do | ||
1847 | case *ssa.Select: | ||
1848 | // nothing to do | ||
1849 | case *ssa.ChangeInterface: | ||
1850 | // nothing to do | ||
1851 | case *ssa.Go: | ||
1852 | // nothing to do, handled generically by operands | ||
1853 | case *ssa.Defer: | ||
1854 | // nothing to do, handled generically by operands | ||
1855 | default: | ||
1856 | panic(fmt.Sprintf("unreachable: %T", instr)) | ||
1857 | } | ||
1858 | } | ||
1859 | } | ||
1860 | } | ||
1861 | |||
1862 | // isNoCopyType reports whether a type represents the NoCopy sentinel | ||
1863 | // type. The NoCopy type is a named struct with no fields and exactly | ||
1864 | // one method `func Lock()` that is empty. | ||
1865 | // | ||
1866 | // FIXME(dh): currently we're not checking that the function body is | ||
1867 | // empty. | ||
1868 | func isNoCopyType(typ types.Type) bool { | ||
1869 | st, ok := typ.Underlying().(*types.Struct) | ||
1870 | if !ok { | ||
1871 | return false | ||
1872 | } | ||
1873 | if st.NumFields() != 0 { | ||
1874 | return false | ||
1875 | } | ||
1876 | |||
1877 | named, ok := typ.(*types.Named) | ||
1878 | if !ok { | ||
1879 | return false | ||
1880 | } | ||
1881 | if named.NumMethods() != 1 { | ||
1882 | return false | ||
1883 | } | ||
1884 | meth := named.Method(0) | ||
1885 | if meth.Name() != "Lock" { | ||
1886 | return false | ||
1887 | } | ||
1888 | sig := meth.Type().(*types.Signature) | ||
1889 | if sig.Params().Len() != 0 || sig.Results().Len() != 0 { | ||
1890 | return false | ||
1891 | } | ||
1892 | return true | ||
1893 | } | ||
1894 | |||
1895 | func walkPhi(v ssa.Value, fn func(v ssa.Value)) { | ||
1896 | phi, ok := v.(*ssa.Phi) | ||
1897 | if !ok { | ||
1898 | fn(v) | ||
1899 | return | ||
1900 | } | ||
1901 | |||
1902 | seen := map[ssa.Value]struct{}{} | ||
1903 | var impl func(v *ssa.Phi) | ||
1904 | impl = func(v *ssa.Phi) { | ||
1905 | if _, ok := seen[v]; ok { | ||
1906 | return | ||
1907 | } | ||
1908 | seen[v] = struct{}{} | ||
1909 | for _, e := range v.Edges { | ||
1910 | if ev, ok := e.(*ssa.Phi); ok { | ||
1911 | impl(ev) | ||
1912 | } else { | ||
1913 | fn(e) | ||
1914 | } | ||
1915 | } | ||
1916 | } | ||
1917 | impl(phi) | ||
1918 | } | ||
1919 | |||
1920 | func interfacesFromExportData(pkg *types.Package) []*types.Interface { | ||
1921 | var out []*types.Interface | ||
1922 | scope := pkg.Scope() | ||
1923 | for _, name := range scope.Names() { | ||
1924 | obj := scope.Lookup(name) | ||
1925 | out = append(out, interfacesFromObject(obj)...) | ||
1926 | } | ||
1927 | return out | ||
1928 | } | ||
1929 | |||
1930 | func interfacesFromObject(obj types.Object) []*types.Interface { | ||
1931 | var out []*types.Interface | ||
1932 | switch obj := obj.(type) { | ||
1933 | case *types.Func: | ||
1934 | sig := obj.Type().(*types.Signature) | ||
1935 | for i := 0; i < sig.Results().Len(); i++ { | ||
1936 | out = append(out, interfacesFromObject(sig.Results().At(i))...) | ||
1937 | } | ||
1938 | for i := 0; i < sig.Params().Len(); i++ { | ||
1939 | out = append(out, interfacesFromObject(sig.Params().At(i))...) | ||
1940 | } | ||
1941 | case *types.TypeName: | ||
1942 | if named, ok := obj.Type().(*types.Named); ok { | ||
1943 | for i := 0; i < named.NumMethods(); i++ { | ||
1944 | out = append(out, interfacesFromObject(named.Method(i))...) | ||
1945 | } | ||
1946 | |||
1947 | if iface, ok := named.Underlying().(*types.Interface); ok { | ||
1948 | out = append(out, iface) | ||
1949 | } | ||
1950 | } | ||
1951 | case *types.Var: | ||
1952 | // No call to Underlying here. We want unnamed interfaces | ||
1953 | // only. Named interfaces are gotten directly from the | ||
1954 | // package's scope. | ||
1955 | if iface, ok := obj.Type().(*types.Interface); ok { | ||
1956 | out = append(out, iface) | ||
1957 | } | ||
1958 | case *types.Const: | ||
1959 | case *types.Builtin: | ||
1960 | default: | ||
1961 | panic(fmt.Sprintf("unhandled type: %T", obj)) | ||
1962 | } | ||
1963 | return out | ||
1964 | } | ||