internal/godoc/internal/doc: remove unused declarations from playable examples

When synthesizing the code for a playable example, we need to include
all the declared variables, types and functions that the example uses.
This CL makes sure we don't include more than we need.

If a declaration declared multiple names, like `var a, b int` or
```
var (
    a int
    b string
)
```
then we would include the entire declaration, even if the example only
used one of the variables.

Keep track the names actually used, and prune the unused ones from the
declarations.

For golang/go#43658

Change-Id: Ia9e88ecd6d4ea50bd07fad6dfba219bfcb512f0d
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/285972
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/internal/godoc/internal/doc/example.go b/internal/godoc/internal/doc/example.go
index 96b8c8f..9a09f41 100644
--- a/internal/godoc/internal/doc/example.go
+++ b/internal/godoc/internal/doc/example.go
@@ -304,6 +304,7 @@
 	var depDecls []ast.Decl
 	unresolved := make(map[string]bool)
 	hasDepDecls := make(map[ast.Decl]bool)
+	objs := map[*ast.Object]bool{}
 
 	var inspectFunc func(ast.Node) bool
 	inspectFunc = func(n ast.Node) bool {
@@ -312,6 +313,7 @@
 			if e.Obj == nil && e.Name != "_" {
 				unresolved[e.Name] = true
 			} else if d := topDecls[e.Obj]; d != nil {
+				objs[e.Obj] = true
 				if !hasDepDecls[d] {
 					hasDepDecls[d] = true
 					depDecls = append(depDecls, d)
@@ -367,7 +369,54 @@
 			}
 		}
 	}
-	return depDecls, unresolved
+	// Some decls include multiple specs, such as a variable declaration with
+	// multiple variables on the same line, or a parenthesized declaration. Trim
+	// the declarations to include only the specs that are actually mentioned.
+	ds := depDecls[:0]
+	for _, d := range depDecls {
+		switch d := d.(type) {
+		case *ast.FuncDecl:
+			ds = append(ds, d)
+		case *ast.GenDecl:
+			// Collect all Specs that were mentioned in the example.
+			var specs []ast.Spec
+			for _, s := range d.Specs {
+				switch s := s.(type) {
+				case *ast.TypeSpec:
+					if objs[s.Name.Obj] {
+						specs = append(specs, s)
+					}
+				case *ast.ValueSpec:
+					// A ValueSpec may have multiple names (e.g. "var a, b int").
+					// Keep only the names that were mentioned in the example.
+					ns := *s
+					ns.Names = nil
+					ns.Values = nil
+					for i, n := range s.Names {
+						if objs[n.Obj] {
+							ns.Names = append(ns.Names, n)
+							if s.Values != nil {
+								ns.Values = append(ns.Values, s.Values[i])
+							}
+						}
+					}
+					if len(ns.Names) > 0 {
+						specs = append(specs, &ns)
+					}
+				}
+			}
+			if len(specs) > 0 {
+				nd := *d // copy the GenDecl
+				nd.Specs = specs
+				if len(specs) == 1 {
+					// Remove grouping parens if there is only one spec.
+					nd.Lparen = 0
+				}
+				ds = append(ds, &nd)
+			}
+		}
+	}
+	return ds, unresolved
 }
 
 // synthesizeImportDecl creates the imports for the example. We want the imports
diff --git a/internal/godoc/internal/doc/testdata/examples/issue43658.go b/internal/godoc/internal/doc/testdata/examples/issue43658.go
index ccc1638..385223a 100644
--- a/internal/godoc/internal/doc/testdata/examples/issue43658.go
+++ b/internal/godoc/internal/doc/testdata/examples/issue43658.go
@@ -82,68 +82,70 @@
 	return s
 }
 
-var smallDumbell = []intset{
-	0: linksTo(1, 2),
-	1: linksTo(2),
-	2: linksTo(3),
-	3: linksTo(4, 5),
-	4: linksTo(5),
-	5: nil,
-}
+var (
+	smallDumbell = []intset{
+		0: linksTo(1, 2),
+		1: linksTo(2),
+		2: linksTo(3),
+		3: linksTo(4, 5),
+		4: linksTo(5),
+		5: nil,
+	}
 
-// http://www.slate.com/blogs/the_world_/2014/07/17/the_middle_east_friendship_chart.html
-var middleEast = struct{ friends, complicated, enemies []intset }{
-	// green cells
-	friends: []intset{
-		0:  nil,
-		1:  linksTo(5, 7, 9, 12),
-		2:  linksTo(11),
-		3:  linksTo(4, 5, 10),
-		4:  linksTo(3, 5, 10),
-		5:  linksTo(1, 3, 4, 8, 10, 12),
-		6:  nil,
-		7:  linksTo(1, 12),
-		8:  linksTo(5, 9, 11),
-		9:  linksTo(1, 8, 12),
-		10: linksTo(3, 4, 5),
-		11: linksTo(2, 8),
-		12: linksTo(1, 5, 7, 9),
-	},
+	// http://www.slate.com/blogs/the_world_/2014/07/17/the_middle_east_friendship_chart.html
+	middleEast = struct{ friends, complicated, enemies []intset }{
+		// green cells
+		friends: []intset{
+			0:  nil,
+			1:  linksTo(5, 7, 9, 12),
+			2:  linksTo(11),
+			3:  linksTo(4, 5, 10),
+			4:  linksTo(3, 5, 10),
+			5:  linksTo(1, 3, 4, 8, 10, 12),
+			6:  nil,
+			7:  linksTo(1, 12),
+			8:  linksTo(5, 9, 11),
+			9:  linksTo(1, 8, 12),
+			10: linksTo(3, 4, 5),
+			11: linksTo(2, 8),
+			12: linksTo(1, 5, 7, 9),
+		},
 
-	// yellow cells
-	complicated: []intset{
-		0:  linksTo(2, 4),
-		1:  linksTo(4, 8),
-		2:  linksTo(0, 3, 4, 5, 8, 9),
-		3:  linksTo(2, 8, 11),
-		4:  linksTo(0, 1, 2, 8),
-		5:  linksTo(2),
-		6:  nil,
-		7:  linksTo(9, 11),
-		8:  linksTo(1, 2, 3, 4, 10, 12),
-		9:  linksTo(2, 7, 11),
-		10: linksTo(8),
-		11: linksTo(3, 7, 9, 12),
-		12: linksTo(8, 11),
-	},
+		// yellow cells
+		complicated: []intset{
+			0:  linksTo(2, 4),
+			1:  linksTo(4, 8),
+			2:  linksTo(0, 3, 4, 5, 8, 9),
+			3:  linksTo(2, 8, 11),
+			4:  linksTo(0, 1, 2, 8),
+			5:  linksTo(2),
+			6:  nil,
+			7:  linksTo(9, 11),
+			8:  linksTo(1, 2, 3, 4, 10, 12),
+			9:  linksTo(2, 7, 11),
+			10: linksTo(8),
+			11: linksTo(3, 7, 9, 12),
+			12: linksTo(8, 11),
+		},
 
-	// red cells
-	enemies: []intset{
-		0:  linksTo(1, 3, 5, 6, 7, 8, 9, 10, 11, 12),
-		1:  linksTo(0, 2, 3, 6, 10, 11),
-		2:  linksTo(1, 6, 7, 10, 12),
-		3:  linksTo(0, 1, 6, 7, 9, 12),
-		4:  linksTo(6, 7, 9, 11, 12),
-		5:  linksTo(0, 6, 7, 9, 11),
-		6:  linksTo(0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12),
-		7:  linksTo(0, 2, 3, 4, 5, 6, 8, 10),
-		8:  linksTo(0, 6, 7),
-		9:  linksTo(0, 3, 4, 5, 6, 10),
-		10: linksTo(0, 1, 2, 6, 7, 9, 11, 12),
-		11: linksTo(0, 1, 4, 5, 6, 10),
-		12: linksTo(0, 2, 3, 4, 6, 10),
-	},
-}
+		// red cells
+		enemies: []intset{
+			0:  linksTo(1, 3, 5, 6, 7, 8, 9, 10, 11, 12),
+			1:  linksTo(0, 2, 3, 6, 10, 11),
+			2:  linksTo(1, 6, 7, 10, 12),
+			3:  linksTo(0, 1, 6, 7, 9, 12),
+			4:  linksTo(6, 7, 9, 11, 12),
+			5:  linksTo(0, 6, 7, 9, 11),
+			6:  linksTo(0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12),
+			7:  linksTo(0, 2, 3, 4, 5, 6, 8, 10),
+			8:  linksTo(0, 6, 7),
+			9:  linksTo(0, 3, 4, 5, 6, 10),
+			10: linksTo(0, 1, 2, 6, 7, 9, 11, 12),
+			11: linksTo(0, 1, 4, 5, 6, 10),
+			12: linksTo(0, 2, 3, 4, 6, 10),
+		},
+	}
+)
 
 var friends, enemies *simple.WeightedUndirectedGraph