internal/frontend: signal doc: ignore deprecated

If a package is marked deprecated, don't require its symbols to be
documented.

Example: golang.org/x/crypto/cast5.

We don't need to do that for individual symbols, because if they have a
deprecation comment, then they already have a comment!

For golang/go#80385.

Change-Id: I5a3bde529fa01e9503236c2f9231c19ba4f2c500
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/808740
Reviewed-by: Ethan Lee <ethanalee@google.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 d4987d0..bc171db 100644
--- a/internal/frontend/evals.go
+++ b/internal/frontend/evals.go
@@ -20,6 +20,7 @@
 	"golang.org/x/mod/semver"
 	"golang.org/x/pkgsite/internal"
 	"golang.org/x/pkgsite/internal/godoc"
+	"golang.org/x/pkgsite/internal/godoc/dochtml"
 	"golang.org/x/pkgsite/internal/version"
 )
 
@@ -288,6 +289,12 @@
 // API surface. It does not enforce standard Go documentation formatting
 // (e.g., "Name does...").
 func collectSymbols(files []*ast.File, add func(name string, has bool)) {
+	for _, file := range files {
+		if file != nil && file.Doc != nil && dochtml.IsDeprecated(file.Doc.Text()) {
+			return
+		}
+	}
+
 	ifaceMethods := collectInterfaceMethods(files)
 
 	// specDoc finds the doc comment for a spec. Typically this will be doc itself,
diff --git a/internal/frontend/evals_test.go b/internal/frontend/evals_test.go
index f391e11..a6f8e48 100644
--- a/internal/frontend/evals_test.go
+++ b/internal/frontend/evals_test.go
@@ -327,6 +327,56 @@
 		})
 	}
 }
+
+func TestCollectSymbolsDeprecatedPackage(t *testing.T) {
+	testCases := []struct {
+		name string
+		src  string
+		want map[string]bool
+	}{
+		{
+			name: "deprecated package",
+			src: `// Deprecated: use another package.
+package foo
+
+// Foo does something.
+func Foo() {}
+`,
+			want: map[string]bool{},
+		},
+		{
+			name: "deprecated not at start of paragraph",
+			src: `// Package foo is active.
+// Deprecated: this is not a new paragraph.
+package foo
+
+// Foo does something.
+func Foo() {}
+`,
+			want: map[string]bool{"Foo": true},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			fset := token.NewFileSet()
+			f, err := parser.ParseFile(fset, "foo.go", tc.src, parser.ParseComments)
+			if err != nil {
+				t.Fatalf("parser.ParseFile: %v", err)
+			}
+
+			got := make(map[string]bool)
+			collectSymbols([]*ast.File{f}, func(name string, has bool) {
+				got[name] = has
+			})
+
+			if diff := cmp.Diff(tc.want, got); diff != "" {
+				t.Errorf("collectSymbols() mismatch (-want +got):\n%s", diff)
+			}
+		})
+	}
+}
+
 func TestSigString(t *testing.T) {
 	testCases := []struct {
 		src  string
diff --git a/internal/godoc/dochtml/deprecated.go b/internal/godoc/dochtml/deprecated.go
index a732ad8..ff20b29 100644
--- a/internal/godoc/dochtml/deprecated.go
+++ b/internal/godoc/dochtml/deprecated.go
@@ -12,19 +12,19 @@
 // "Deprecated:" at the start of a paragraph.
 var deprecatedRx = regexp.MustCompile(`(^|\n\s*\n)\s*Deprecated:`)
 
-// isDeprecated reports whether the string has a "Deprecated" line.
-func isDeprecated(s string) bool {
+// IsDeprecated reports whether the string has a "Deprecated" line.
+func IsDeprecated(s string) bool {
 	return deprecatedRx.MatchString(s)
 }
 
 func typeIsDeprecated(t *doc.Type) bool {
-	return isDeprecated(t.Doc)
+	return IsDeprecated(t.Doc)
 }
 
 func valueIsDeprecated(v *doc.Value) bool {
-	return isDeprecated(v.Doc)
+	return IsDeprecated(v.Doc)
 }
 
 func funcIsDeprecated(f *doc.Func) bool {
-	return isDeprecated(f.Doc)
+	return IsDeprecated(f.Doc)
 }
diff --git a/internal/godoc/dochtml/deprecated_test.go b/internal/godoc/dochtml/deprecated_test.go
index 464ad41..8b6b908 100644
--- a/internal/godoc/dochtml/deprecated_test.go
+++ b/internal/godoc/dochtml/deprecated_test.go
@@ -19,7 +19,7 @@
 		{"line 1\nDeprecated:\nline 2\n", false},
 		{"line 1\n\nDeprecated:\nline 2\n", true},
 	} {
-		got := isDeprecated(test.text)
+		got := IsDeprecated(test.text)
 		if got != test.want {
 			t.Errorf("%q: got %t, want %t", test.text, got, test.want)
 		}