passes/unusedwrites: Add TODO for how to handle generics.

Adds a TODO to explain how to support generics, e.g. fn._Instantiations() should be included once available.

Refactors the run() function to make this simple in the future.

Updates golang/go#52503

Change-Id: Iec84f9bf200cab1026b19e1962165102be0a85ef
Reviewed-on: https://go-review.googlesource.com/c/tools/+/403355
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
gopls-CI: kokoro <noreply+kokoro@google.com>
Run-TryBot: Guodong Li <guodongli@google.com>
Run-TryBot: Tim King <taking@google.com>
Reviewed-by: Guodong Li <guodongli@google.com>
Auto-Submit: Tim King <taking@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
diff --git a/go/analysis/passes/unusedwrite/unusedwrite.go b/go/analysis/passes/unusedwrite/unusedwrite.go
index 4aad083..9cc45e0 100644
--- a/go/analysis/passes/unusedwrite/unusedwrite.go
+++ b/go/analysis/passes/unusedwrite/unusedwrite.go
@@ -50,40 +50,49 @@
 }
 
 func run(pass *analysis.Pass) (interface{}, error) {
-	// Check the writes to struct and array objects.
-	checkStore := func(store *ssa.Store) {
-		// Consider field/index writes to an object whose elements are copied and not shared.
-		// MapUpdate is excluded since only the reference of the map is copied.
-		switch addr := store.Addr.(type) {
-		case *ssa.FieldAddr:
-			if isDeadStore(store, addr.X, addr) {
-				// Report the bug.
+	ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
+	for _, fn := range ssainput.SrcFuncs {
+		// TODO(taking): Iterate over fn._Instantiations() once exported. If so, have 1 report per Pos().
+		reports := checkStores(fn)
+		for _, store := range reports {
+			switch addr := store.Addr.(type) {
+			case *ssa.FieldAddr:
 				pass.Reportf(store.Pos(),
 					"unused write to field %s",
 					getFieldName(addr.X.Type(), addr.Field))
-			}
-		case *ssa.IndexAddr:
-			if isDeadStore(store, addr.X, addr) {
-				// Report the bug.
+			case *ssa.IndexAddr:
 				pass.Reportf(store.Pos(),
 					"unused write to array index %s", addr.Index)
 			}
 		}
 	}
+	return nil, nil
+}
 
-	ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA)
-	for _, fn := range ssainput.SrcFuncs {
-		// Visit each block. No need to visit fn.Recover.
-		for _, blk := range fn.Blocks {
-			for _, instr := range blk.Instrs {
-				// Identify writes.
-				if store, ok := instr.(*ssa.Store); ok {
-					checkStore(store)
+// checkStores returns *Stores in fn whose address is written to but never used.
+func checkStores(fn *ssa.Function) []*ssa.Store {
+	var reports []*ssa.Store
+	// Visit each block. No need to visit fn.Recover.
+	for _, blk := range fn.Blocks {
+		for _, instr := range blk.Instrs {
+			// Identify writes.
+			if store, ok := instr.(*ssa.Store); ok {
+				// Consider field/index writes to an object whose elements are copied and not shared.
+				// MapUpdate is excluded since only the reference of the map is copied.
+				switch addr := store.Addr.(type) {
+				case *ssa.FieldAddr:
+					if isDeadStore(store, addr.X, addr) {
+						reports = append(reports, store)
+					}
+				case *ssa.IndexAddr:
+					if isDeadStore(store, addr.X, addr) {
+						reports = append(reports, store)
+					}
 				}
 			}
 		}
 	}
-	return nil, nil
+	return reports
 }
 
 // isDeadStore determines whether a field/index write to an object is dead.