internal/frontend: use collectSymbols to count

Use connectSymbols to populate docSummary.

(Also, add a TODO regarding docs for main packages, suggested by
a reviewer on an earlier CL.)

For golang/go#80385.

Change-Id: I4f893f5814072371f4460f0f9f565f24ca2ea7a2
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/805321
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
kokoro-CI: kokoro <noreply+kokoro@google.com>
diff --git a/internal/frontend/evals.go b/internal/frontend/evals.go
index 6f9a8f3..4e3b1bc 100644
--- a/internal/frontend/evals.go
+++ b/internal/frontend/evals.go
@@ -138,7 +138,9 @@
 // docSummary is a summary of a package's documentation, intended
 // for creating an evaluation.
 type docSummary struct {
-	packageHasDoc bool
+	packageHasDoc      bool
+	numExportedSymbols int // that need documentation
+	numHaveDoc         int // number of exported symbols that need doc and have it
 }
 
 // summarizeDocumentation produces a docSummary for the given package.
@@ -169,6 +171,10 @@
 
 	// Main package? Nothing to do (we don't insist
 	// that mains have doc.)
+	// TODO(jba): consider checking the documentation of main packages.
+	// They should at least have a package doc.
+	// On the other hand, many main packages have an extensive README.md but
+	// no package doc.
 	pkgName := files[0].Name.Name
 	if pkgName == "main" {
 		return summary
@@ -181,7 +187,13 @@
 		}
 	}
 
-	// TODO: count symbols with doc.
+	// Count exported symbols with/without doc.
+	collectSymbols(files, func(_ string, has bool) {
+		summary.numExportedSymbols++
+		if has {
+			summary.numHaveDoc++
+		}
+	})
 
 	return summary
 }
diff --git a/internal/frontend/evals_test.go b/internal/frontend/evals_test.go
index cb44bb7..6a8d3ce 100644
--- a/internal/frontend/evals_test.go
+++ b/internal/frontend/evals_test.go
@@ -223,6 +223,90 @@
 				packageHasDoc: false,
 			},
 		},
+		{
+			name: "all exported symbols documented",
+			files: map[string]string{
+				"mypkg.go": `// Package mypkg provides utilities.
+package mypkg
+
+// F is a function.
+func F() {}
+
+// T is a type.
+type T int
+`,
+			},
+			want: docSummary{
+				packageHasDoc:      true,
+				numExportedSymbols: 2,
+				numHaveDoc:         2,
+			},
+		},
+		{
+			name: "no exported symbols documented",
+			files: map[string]string{
+				"mypkg.go": `package mypkg
+
+func F() {}
+
+type T int
+`,
+			},
+			want: docSummary{
+				packageHasDoc:      false,
+				numExportedSymbols: 2,
+				numHaveDoc:         0,
+			},
+		},
+		{
+			name: "mixed documented and undocumented exported symbols",
+			files: map[string]string{
+				"mypkg.go": `// Package mypkg provides utilities.
+package mypkg
+
+// ExportedWithDoc has doc comment.
+func ExportedWithDoc() {}
+
+func ExportedNoDoc() {}
+
+// ExportedType has doc.
+type ExportedType struct{}
+
+func (ExportedType) UndocumentedMethod() {}
+
+type unexportedType struct{}
+
+func (unexportedType) MethodOnUnexported() {}
+`,
+			},
+			want: docSummary{
+				packageHasDoc:      true,
+				numExportedSymbols: 4,
+				numHaveDoc:         2,
+			},
+		},
+		{
+			name: "symbols across multiple files",
+			files: map[string]string{
+				"doc.go": `// Package mypkg provides utilities.
+package mypkg
+
+// Helper function.
+func Helper() {}
+`,
+				"types.go": `package mypkg
+
+type Config struct{}
+
+const DefaultTimeout = 10
+`,
+			},
+			want: docSummary{
+				packageHasDoc:      true,
+				numExportedSymbols: 3,
+				numHaveDoc:         1,
+			},
+		},
 	}
 
 	for _, tc := range testCases {