internal/analysisinternal: rationalize dependencies

This change moves all declarations in the analysisinternal
package into one of two subpackages:

- driverutil, for implementation helper functions for drivers
  such as unitchecker, {single,multi}checker, and analysistest; and
- analyzerutil, for helper functions for Analyzers.

A number of declarations from go/analysis/internal/analysisflags
also moved into driverlib. analysisflags is now concerned only
with command-line flag parsing.

No behavior changes.

A followup CL will rename analysisinternal to just analysis
since it is now just a path segment, not a package to be imported.

Change-Id: Ibdd36aa80f6d8faf17938e715c05a8efb36f2195
Reviewed-on: https://go-review.googlesource.com/c/tools/+/718081
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Robert Findley <rfindley@google.com>
diff --git a/go/analysis/analysistest/analysistest.go b/go/analysis/analysistest/analysistest.go
index 3862a21..56b3afd 100644
--- a/go/analysis/analysistest/analysistest.go
+++ b/go/analysis/analysistest/analysistest.go
@@ -27,8 +27,8 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/checker"
 	"golang.org/x/tools/go/analysis/internal"
-	"golang.org/x/tools/go/analysis/internal/analysisflags"
 	"golang.org/x/tools/go/packages"
+	"golang.org/x/tools/internal/analysisinternal/driverutil"
 	"golang.org/x/tools/internal/diff"
 	"golang.org/x/tools/internal/testenv"
 	"golang.org/x/tools/txtar"
@@ -297,7 +297,7 @@
 	if err != nil {
 		return fmt.Errorf("%s: error applying fixes: %v (see possible explanations at RunWithSuggestedFixes)", filename, err)
 	}
-	fixed, err := analysisflags.FormatSourceRemoveImports(pkg, fixedBytes)
+	fixed, err := driverutil.FormatSourceRemoveImports(pkg, fixedBytes)
 	if err != nil {
 		return fmt.Errorf("%s: error formatting resulting source: %v\n%s", filename, err, fixedBytes)
 	}
diff --git a/go/analysis/checker/checker.go b/go/analysis/checker/checker.go
index 14f5225..3e2dccf 100644
--- a/go/analysis/checker/checker.go
+++ b/go/analysis/checker/checker.go
@@ -45,9 +45,8 @@
 
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/internal"
-	"golang.org/x/tools/go/analysis/internal/analysisflags"
 	"golang.org/x/tools/go/packages"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/driverutil"
 )
 
 // Options specifies options that control the analysis driver.
@@ -60,7 +59,7 @@
 	// TODO(adonovan): expose ReadFile so that an Overlay specified
 	// in the [packages.Config] can be communicated via
 	// Pass.ReadFile to each Analyzer.
-	readFile analysisinternal.ReadFileFunc
+	readFile driverutil.ReadFileFunc
 }
 
 // Graph holds the results of a round of analysis, including the graph
@@ -333,7 +332,7 @@
 		ResultOf: inputs,
 		Report: func(d analysis.Diagnostic) {
 			// Assert that SuggestedFixes are well formed.
-			if err := analysisinternal.ValidateFixes(act.Package.Fset, act.Analyzer, d.SuggestedFixes); err != nil {
+			if err := driverutil.ValidateFixes(act.Package.Fset, act.Analyzer, d.SuggestedFixes); err != nil {
 				panic(err)
 			}
 			act.Diagnostics = append(act.Diagnostics, d)
@@ -349,7 +348,7 @@
 	if act.opts.readFile != nil {
 		readFile = act.opts.readFile
 	}
-	pass.ReadFile = analysisinternal.CheckedReadFile(pass, readFile)
+	pass.ReadFile = driverutil.CheckedReadFile(pass, readFile)
 	act.pass = pass
 
 	act.Result, act.Err = func() (any, error) {
@@ -371,7 +370,7 @@
 
 		// resolve diagnostic URLs
 		for i := range act.Diagnostics {
-			url, err := analysisflags.ResolveURL(act.Analyzer, act.Diagnostics[i])
+			url, err := driverutil.ResolveURL(act.Analyzer, act.Diagnostics[i])
 			if err != nil {
 				return nil, err
 			}
diff --git a/go/analysis/checker/print.go b/go/analysis/checker/print.go
index d7c0430..9149b87 100644
--- a/go/analysis/checker/print.go
+++ b/go/analysis/checker/print.go
@@ -14,7 +14,7 @@
 	"io"
 
 	"golang.org/x/tools/go/analysis"
-	"golang.org/x/tools/go/analysis/internal/analysisflags"
+	"golang.org/x/tools/internal/analysisinternal/driverutil"
 )
 
 // PrintText emits diagnostics as plain text to w.
@@ -58,7 +58,7 @@
 				}
 				seen[k] = true
 
-				analysisflags.PrintPlain(buf, act.Package.Fset, contextLines, diag)
+				driverutil.PrintPlain(buf, act.Package.Fset, contextLines, diag)
 			}
 		}
 		return nil
@@ -75,7 +75,7 @@
 }
 
 func writeJSONDiagnostics(w io.Writer, roots []*Action) error {
-	tree := make(analysisflags.JSONTree)
+	tree := make(driverutil.JSONTree)
 	forEach(roots, func(act *Action) error {
 		var diags []analysis.Diagnostic
 		if act.IsRoot {
diff --git a/go/analysis/internal/analysisflags/flags.go b/go/analysis/internal/analysisflags/flags.go
index ffc4169..c7637df 100644
--- a/go/analysis/internal/analysisflags/flags.go
+++ b/go/analysis/internal/analysisflags/flags.go
@@ -13,22 +13,20 @@
 	"encoding/json"
 	"flag"
 	"fmt"
-	"go/token"
 	"io"
 	"log"
 	"os"
 	"strconv"
-	"strings"
 
 	"golang.org/x/tools/go/analysis"
 )
 
 // flags common to all {single,multi,unit}checkers.
 var (
-	JSON     = false // -json
-	Context  = -1    // -c=N: if N>0, display offending line plus N lines of context
-	Fix      bool    // -fix
-	diffFlag bool    // -diff (changes [ApplyFixes] behavior)
+	JSON    = false // -json
+	Context = -1    // -c=N: if N>0, display offending line plus N lines of context
+	Fix     bool    // -fix
+	Diff    bool    // -diff
 )
 
 // Parse creates a flag for each of the analyzer's flags,
@@ -78,7 +76,7 @@
 	flag.BoolVar(&JSON, "json", JSON, "emit JSON output")
 	flag.IntVar(&Context, "c", Context, `display offending line with this many lines of context`)
 	flag.BoolVar(&Fix, "fix", false, "apply all suggested fixes")
-	flag.BoolVar(&diffFlag, "diff", false, "with -fix, don't update the files, but print a unified diff")
+	flag.BoolVar(&Diff, "diff", false, "with -fix, don't update the files, but print a unified diff")
 
 	// Add shims for legacy vet flags to enable existing
 	// scripts that run vet to continue to work.
@@ -310,150 +308,3 @@
 	"unusedfuncs":         "unusedresult.funcs",
 	"unusedstringmethods": "unusedresult.stringmethods",
 }
-
-// ---- output helpers common to all drivers ----
-//
-// These functions should not depend on global state (flags)!
-// Really they belong in a different package.
-
-// TODO(adonovan): don't accept an io.Writer if we don't report errors.
-// Either accept a bytes.Buffer (infallible), or return a []byte.
-
-// PrintPlain prints a diagnostic in plain text form.
-// If contextLines is nonnegative, it also prints the
-// offending line plus this many lines of context.
-func PrintPlain(out io.Writer, fset *token.FileSet, contextLines int, diag analysis.Diagnostic) {
-	print := func(pos, end token.Pos, message string) {
-		posn := fset.Position(pos)
-		fmt.Fprintf(out, "%s: %s\n", posn, message)
-
-		// show offending line plus N lines of context.
-		if contextLines >= 0 {
-			end := fset.Position(end)
-			if !end.IsValid() {
-				end = posn
-			}
-			// TODO(adonovan): highlight the portion of the line indicated
-			// by pos...end using ASCII art, terminal colors, etc?
-			data, _ := os.ReadFile(posn.Filename)
-			lines := strings.Split(string(data), "\n")
-			for i := posn.Line - contextLines; i <= end.Line+contextLines; i++ {
-				if 1 <= i && i <= len(lines) {
-					fmt.Fprintf(out, "%d\t%s\n", i, lines[i-1])
-				}
-			}
-		}
-	}
-
-	print(diag.Pos, diag.End, diag.Message)
-	for _, rel := range diag.Related {
-		print(rel.Pos, rel.End, "\t"+rel.Message)
-	}
-}
-
-// A JSONTree is a mapping from package ID to analysis name to result.
-// Each result is either a jsonError or a list of JSONDiagnostic.
-type JSONTree map[string]map[string]any
-
-// A TextEdit describes the replacement of a portion of a file.
-// Start and End are zero-based half-open indices into the original byte
-// sequence of the file, and New is the new text.
-type JSONTextEdit struct {
-	Filename string `json:"filename"`
-	Start    int    `json:"start"`
-	End      int    `json:"end"`
-	New      string `json:"new"`
-}
-
-// A JSONSuggestedFix describes an edit that should be applied as a whole or not
-// at all. It might contain multiple TextEdits/text_edits if the SuggestedFix
-// consists of multiple non-contiguous edits.
-type JSONSuggestedFix struct {
-	Message string         `json:"message"`
-	Edits   []JSONTextEdit `json:"edits"`
-}
-
-// A JSONDiagnostic describes the JSON schema of an analysis.Diagnostic.
-//
-// TODO(matloob): include End position if present.
-type JSONDiagnostic struct {
-	Category       string                   `json:"category,omitempty"`
-	Posn           string                   `json:"posn"` // e.g. "file.go:line:column"
-	Message        string                   `json:"message"`
-	SuggestedFixes []JSONSuggestedFix       `json:"suggested_fixes,omitempty"`
-	Related        []JSONRelatedInformation `json:"related,omitempty"`
-}
-
-// A JSONRelated describes a secondary position and message related to
-// a primary diagnostic.
-//
-// TODO(adonovan): include End position if present.
-type JSONRelatedInformation struct {
-	Posn    string `json:"posn"` // e.g. "file.go:line:column"
-	Message string `json:"message"`
-}
-
-// Add adds the result of analysis 'name' on package 'id'.
-// The result is either a list of diagnostics or an error.
-func (tree JSONTree) Add(fset *token.FileSet, id, name string, diags []analysis.Diagnostic, err error) {
-	var v any
-	if err != nil {
-		type jsonError struct {
-			Err string `json:"error"`
-		}
-		v = jsonError{err.Error()}
-	} else if len(diags) > 0 {
-		diagnostics := make([]JSONDiagnostic, 0, len(diags))
-		for _, f := range diags {
-			var fixes []JSONSuggestedFix
-			for _, fix := range f.SuggestedFixes {
-				var edits []JSONTextEdit
-				for _, edit := range fix.TextEdits {
-					edits = append(edits, JSONTextEdit{
-						Filename: fset.Position(edit.Pos).Filename,
-						Start:    fset.Position(edit.Pos).Offset,
-						End:      fset.Position(edit.End).Offset,
-						New:      string(edit.NewText),
-					})
-				}
-				fixes = append(fixes, JSONSuggestedFix{
-					Message: fix.Message,
-					Edits:   edits,
-				})
-			}
-			var related []JSONRelatedInformation
-			for _, r := range f.Related {
-				related = append(related, JSONRelatedInformation{
-					Posn:    fset.Position(r.Pos).String(),
-					Message: r.Message,
-				})
-			}
-			jdiag := JSONDiagnostic{
-				Category:       f.Category,
-				Posn:           fset.Position(f.Pos).String(),
-				Message:        f.Message,
-				SuggestedFixes: fixes,
-				Related:        related,
-			}
-			diagnostics = append(diagnostics, jdiag)
-		}
-		v = diagnostics
-	}
-	if v != nil {
-		m, ok := tree[id]
-		if !ok {
-			m = make(map[string]any)
-			tree[id] = m
-		}
-		m[name] = v
-	}
-}
-
-func (tree JSONTree) Print(out io.Writer) error {
-	data, err := json.MarshalIndent(tree, "", "\t")
-	if err != nil {
-		log.Panicf("internal error: JSON marshaling failed: %v", err)
-	}
-	_, err = fmt.Fprintf(out, "%s\n", data)
-	return err
-}
diff --git a/go/analysis/internal/checker/checker.go b/go/analysis/internal/checker/checker.go
index 4ba8788..357ef90 100644
--- a/go/analysis/internal/checker/checker.go
+++ b/go/analysis/internal/checker/checker.go
@@ -32,6 +32,7 @@
 	"golang.org/x/tools/go/analysis/internal"
 	"golang.org/x/tools/go/analysis/internal/analysisflags"
 	"golang.org/x/tools/go/packages"
+	"golang.org/x/tools/internal/analysisinternal/driverutil"
 )
 
 var (
@@ -191,10 +192,10 @@
 	// Don't print the diagnostics,
 	// but apply all fixes from the root actions.
 	if analysisflags.Fix {
-		fixActions := make([]analysisflags.FixAction, len(graph.Roots))
+		fixActions := make([]driverutil.FixAction, len(graph.Roots))
 		for i, act := range graph.Roots {
 			if pass := internal.ActionPass(act); pass != nil {
-				fixActions[i] = analysisflags.FixAction{
+				fixActions[i] = driverutil.FixAction{
 					Name:         act.String(),
 					Pkg:          act.Package.Types,
 					FileSet:      act.Package.Fset,
@@ -203,7 +204,7 @@
 				}
 			}
 		}
-		if err := analysisflags.ApplyFixes(fixActions, dbg('v')); err != nil {
+		if err := driverutil.ApplyFixes(fixActions, analysisflags.Diff, dbg('v')); err != nil {
 			// Fail when applying fixes failed.
 			log.Print(err)
 			exitAtLeast(1)
diff --git a/go/analysis/passes/appends/appends.go b/go/analysis/passes/appends/appends.go
index b4e91ed..cabbebc 100644
--- a/go/analysis/passes/appends/appends.go
+++ b/go/analysis/passes/appends/appends.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 //go:embed doc.go
@@ -23,7 +23,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "appends",
-	Doc:      analysisinternal.MustExtractDoc(doc, "appends"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "appends"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/appends",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/asmdecl/asmdecl.go b/go/analysis/passes/asmdecl/asmdecl.go
index e9c0879..36a96be 100644
--- a/go/analysis/passes/asmdecl/asmdecl.go
+++ b/go/analysis/passes/asmdecl/asmdecl.go
@@ -19,7 +19,7 @@
 	"strings"
 
 	"golang.org/x/tools/go/analysis"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 const Doc = "report mismatches between assembly files and Go declarations"
@@ -175,7 +175,7 @@
 
 Files:
 	for _, fname := range sfiles {
-		content, tf, err := analysisinternal.ReadFile(pass, fname)
+		content, tf, err := analyzerutil.ReadFile(pass, fname)
 		if err != nil {
 			return nil, err
 		}
diff --git a/go/analysis/passes/assign/assign.go b/go/analysis/passes/assign/assign.go
index 8080aed..c4ab75d 100644
--- a/go/analysis/passes/assign/assign.go
+++ b/go/analysis/passes/assign/assign.go
@@ -18,7 +18,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/refactor"
 	"golang.org/x/tools/internal/typesinternal"
@@ -29,7 +29,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "assign",
-	Doc:      analysisinternal.MustExtractDoc(doc, "assign"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "assign"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/assign",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/atomic/atomic.go b/go/analysis/passes/atomic/atomic.go
index 9faa3f67..01714ea 100644
--- a/go/analysis/passes/atomic/atomic.go
+++ b/go/analysis/passes/atomic/atomic.go
@@ -13,7 +13,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
@@ -23,7 +23,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:             "atomic",
-	Doc:              analysisinternal.MustExtractDoc(doc, "atomic"),
+	Doc:              analyzerutil.MustExtractDoc(doc, "atomic"),
 	URL:              "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/atomic",
 	Requires:         []*analysis.Analyzer{inspect.Analyzer},
 	RunDespiteErrors: true,
diff --git a/go/analysis/passes/buildtag/buildtag.go b/go/analysis/passes/buildtag/buildtag.go
index 7dd4f24..bf13f68 100644
--- a/go/analysis/passes/buildtag/buildtag.go
+++ b/go/analysis/passes/buildtag/buildtag.go
@@ -14,7 +14,7 @@
 	"unicode"
 
 	"golang.org/x/tools/go/analysis"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 const Doc = "check //go:build and // +build directives"
@@ -86,7 +86,7 @@
 
 	// We cannot use the Go parser, since this may not be a Go source file.
 	// Read the raw bytes instead.
-	content, tf, err := analysisinternal.ReadFile(pass, filename)
+	content, tf, err := analyzerutil.ReadFile(pass, filename)
 	if err != nil {
 		return err
 	}
diff --git a/go/analysis/passes/defers/defers.go b/go/analysis/passes/defers/defers.go
index 3069ee9..69b4ece 100644
--- a/go/analysis/passes/defers/defers.go
+++ b/go/analysis/passes/defers/defers.go
@@ -12,7 +12,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
 
@@ -23,7 +23,7 @@
 var Analyzer = &analysis.Analyzer{
 	Name:     "defers",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
-	Doc:      analysisinternal.MustExtractDoc(doc, "defers"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "defers"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/defers",
 	Run:      run,
 }
diff --git a/go/analysis/passes/directive/directive.go b/go/analysis/passes/directive/directive.go
index c84d258..3a8ce65 100644
--- a/go/analysis/passes/directive/directive.go
+++ b/go/analysis/passes/directive/directive.go
@@ -14,7 +14,7 @@
 	"unicode/utf8"
 
 	"golang.org/x/tools/go/analysis"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 const Doc = `check Go toolchain directives such as //go:debug
@@ -86,7 +86,7 @@
 func checkOtherFile(pass *analysis.Pass, filename string) error {
 	// We cannot use the Go parser, since is not a Go source file.
 	// Read the raw bytes instead.
-	content, tf, err := analysisinternal.ReadFile(pass, filename)
+	content, tf, err := analyzerutil.ReadFile(pass, filename)
 	if err != nil {
 		return err
 	}
diff --git a/go/analysis/passes/framepointer/framepointer.go b/go/analysis/passes/framepointer/framepointer.go
index 809095d..9667ba9 100644
--- a/go/analysis/passes/framepointer/framepointer.go
+++ b/go/analysis/passes/framepointer/framepointer.go
@@ -13,7 +13,7 @@
 	"unicode"
 
 	"golang.org/x/tools/go/analysis"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 const Doc = "report assembly that clobbers the frame pointer before saving it"
@@ -98,7 +98,7 @@
 	}
 
 	for _, fname := range sfiles {
-		content, tf, err := analysisinternal.ReadFile(pass, fname)
+		content, tf, err := analyzerutil.ReadFile(pass, fname)
 		if err != nil {
 			return nil, err
 		}
diff --git a/go/analysis/passes/gofix/gofix.go b/go/analysis/passes/gofix/gofix.go
index bf0418d..b25c4e7 100644
--- a/go/analysis/passes/gofix/gofix.go
+++ b/go/analysis/passes/gofix/gofix.go
@@ -12,7 +12,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/analysis/passes/internal/gofixdirective"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 //go:embed doc.go
@@ -20,7 +20,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "gofixdirective",
-	Doc:      analysisinternal.MustExtractDoc(doc, "gofixdirective"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "gofixdirective"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/gofix",
 	Run:      run,
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
diff --git a/go/analysis/passes/ifaceassert/ifaceassert.go b/go/analysis/passes/ifaceassert/ifaceassert.go
index a6dcf1c..c7cddb7 100644
--- a/go/analysis/passes/ifaceassert/ifaceassert.go
+++ b/go/analysis/passes/ifaceassert/ifaceassert.go
@@ -12,7 +12,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typeparams"
 )
 
@@ -21,7 +21,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "ifaceassert",
-	Doc:      analysisinternal.MustExtractDoc(doc, "ifaceassert"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "ifaceassert"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/ifaceassert",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/inline/inline.go b/go/analysis/passes/inline/inline.go
index 2fe1ead..214126d 100644
--- a/go/analysis/passes/inline/inline.go
+++ b/go/analysis/passes/inline/inline.go
@@ -20,7 +20,7 @@
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/diff"
 	"golang.org/x/tools/internal/moreiters"
@@ -35,7 +35,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name: "inline",
-	Doc:  analysisinternal.MustExtractDoc(doc, "inline"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "inline"),
 	URL:  "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/inline",
 	Run:  run,
 	FactTypes: []analysis.Fact{
diff --git a/go/analysis/passes/loopclosure/loopclosure.go b/go/analysis/passes/loopclosure/loopclosure.go
index 8aeaeee..f95f526 100644
--- a/go/analysis/passes/loopclosure/loopclosure.go
+++ b/go/analysis/passes/loopclosure/loopclosure.go
@@ -13,7 +13,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/packagepath"
 	"golang.org/x/tools/internal/stdlib"
 	"golang.org/x/tools/internal/typesinternal"
@@ -25,7 +25,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "loopclosure",
-	Doc:      analysisinternal.MustExtractDoc(doc, "loopclosure"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "loopclosure"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/loopclosure",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/lostcancel/lostcancel.go b/go/analysis/passes/lostcancel/lostcancel.go
index 72248be..31078cf 100644
--- a/go/analysis/passes/lostcancel/lostcancel.go
+++ b/go/analysis/passes/lostcancel/lostcancel.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/cfg"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
@@ -25,7 +25,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name: "lostcancel",
-	Doc:  analysisinternal.MustExtractDoc(doc, "lostcancel"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "lostcancel"),
 	URL:  "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/lostcancel",
 	Run:  run,
 	Requires: []*analysis.Analyzer{
diff --git a/go/analysis/passes/modernize/any.go b/go/analysis/passes/modernize/any.go
index 3685f57..b1a1940 100644
--- a/go/analysis/passes/modernize/any.go
+++ b/go/analysis/passes/modernize/any.go
@@ -9,14 +9,14 @@
 
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	"golang.org/x/tools/internal/versions"
 )
 
 var AnyAnalyzer = &analysis.Analyzer{
 	Name: "any",
-	Doc:  analysisinternal.MustExtractDoc(doc, "any"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "any"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/bloop.go b/go/analysis/passes/modernize/bloop.go
index 70cfdfe..c1d4c24 100644
--- a/go/analysis/passes/modernize/bloop.go
+++ b/go/analysis/passes/modernize/bloop.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -27,7 +27,7 @@
 
 var BLoopAnalyzer = &analysis.Analyzer{
 	Name: "bloop",
-	Doc:  analysisinternal.MustExtractDoc(doc, "bloop"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "bloop"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/errorsastype.go b/go/analysis/passes/modernize/errorsastype.go
index b3696e2..7adfd4f 100644
--- a/go/analysis/passes/modernize/errorsastype.go
+++ b/go/analysis/passes/modernize/errorsastype.go
@@ -14,7 +14,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -27,7 +27,7 @@
 
 var errorsastypeAnalyzer = &analysis.Analyzer{
 	Name:     "errorsastype",
-	Doc:      analysisinternal.MustExtractDoc(doc, "errorsastype"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "errorsastype"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#errorsastype",
 	Requires: []*analysis.Analyzer{generated.Analyzer, typeindexanalyzer.Analyzer},
 	Run:      errorsastype,
diff --git a/go/analysis/passes/modernize/fmtappendf.go b/go/analysis/passes/modernize/fmtappendf.go
index afc9440..ee2620f 100644
--- a/go/analysis/passes/modernize/fmtappendf.go
+++ b/go/analysis/passes/modernize/fmtappendf.go
@@ -13,7 +13,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/edge"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -23,7 +23,7 @@
 
 var FmtAppendfAnalyzer = &analysis.Analyzer{
 	Name: "fmtappendf",
-	Doc:  analysisinternal.MustExtractDoc(doc, "fmtappendf"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "fmtappendf"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/forvar.go b/go/analysis/passes/modernize/forvar.go
index 20ac3e8..57eb014 100644
--- a/go/analysis/passes/modernize/forvar.go
+++ b/go/analysis/passes/modernize/forvar.go
@@ -10,7 +10,7 @@
 
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/refactor"
@@ -19,7 +19,7 @@
 
 var ForVarAnalyzer = &analysis.Analyzer{
 	Name: "forvar",
-	Doc:  analysisinternal.MustExtractDoc(doc, "forvar"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "forvar"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/maps.go b/go/analysis/passes/modernize/maps.go
index 7ef2a21..9bccedc 100644
--- a/go/analysis/passes/modernize/maps.go
+++ b/go/analysis/passes/modernize/maps.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/refactor"
@@ -26,7 +26,7 @@
 
 var MapsLoopAnalyzer = &analysis.Analyzer{
 	Name: "mapsloop",
-	Doc:  analysisinternal.MustExtractDoc(doc, "mapsloop"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "mapsloop"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/minmax.go b/go/analysis/passes/modernize/minmax.go
index 2877e93..0b5c92d 100644
--- a/go/analysis/passes/modernize/minmax.go
+++ b/go/analysis/passes/modernize/minmax.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -26,7 +26,7 @@
 
 var MinMaxAnalyzer = &analysis.Analyzer{
 	Name: "minmax",
-	Doc:  analysisinternal.MustExtractDoc(doc, "minmax"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "minmax"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/newexpr.go b/go/analysis/passes/modernize/newexpr.go
index 3a3f5a5..f984dab 100644
--- a/go/analysis/passes/modernize/newexpr.go
+++ b/go/analysis/passes/modernize/newexpr.go
@@ -17,14 +17,14 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/versions"
 )
 
 var NewExprAnalyzer = &analysis.Analyzer{
 	Name:      "newexpr",
-	Doc:       analysisinternal.MustExtractDoc(doc, "newexpr"),
+	Doc:       analyzerutil.MustExtractDoc(doc, "newexpr"),
 	URL:       "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/modernize#newexpr",
 	Requires:  []*analysis.Analyzer{inspect.Analyzer},
 	Run:       run,
diff --git a/go/analysis/passes/modernize/omitzero.go b/go/analysis/passes/modernize/omitzero.go
index 34e2ed7..0c14d4e 100644
--- a/go/analysis/passes/modernize/omitzero.go
+++ b/go/analysis/passes/modernize/omitzero.go
@@ -12,7 +12,7 @@
 
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/versions"
@@ -20,7 +20,7 @@
 
 var OmitZeroAnalyzer = &analysis.Analyzer{
 	Name: "omitzero",
-	Doc:  analysisinternal.MustExtractDoc(doc, "omitzero"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "omitzero"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/plusbuild.go b/go/analysis/passes/modernize/plusbuild.go
index e5e2fdd..9b3cd2e 100644
--- a/go/analysis/passes/modernize/plusbuild.go
+++ b/go/analysis/passes/modernize/plusbuild.go
@@ -10,14 +10,14 @@
 	"strings"
 
 	"golang.org/x/tools/go/analysis"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/goplsexport"
 	"golang.org/x/tools/internal/versions"
 )
 
 var plusBuildAnalyzer = &analysis.Analyzer{
 	Name: "plusbuild",
-	Doc:  analysisinternal.MustExtractDoc(doc, "plusbuild"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "plusbuild"),
 	URL:  "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/modernize#plusbuild",
 	Run:  plusbuild,
 }
diff --git a/go/analysis/passes/modernize/rangeint.go b/go/analysis/passes/modernize/rangeint.go
index d887a89..843a8a2 100644
--- a/go/analysis/passes/modernize/rangeint.go
+++ b/go/analysis/passes/modernize/rangeint.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -26,7 +26,7 @@
 
 var RangeIntAnalyzer = &analysis.Analyzer{
 	Name: "rangeint",
-	Doc:  analysisinternal.MustExtractDoc(doc, "rangeint"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "rangeint"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/reflect.go b/go/analysis/passes/modernize/reflect.go
index 6f10d2a..e7c68b4 100644
--- a/go/analysis/passes/modernize/reflect.go
+++ b/go/analysis/passes/modernize/reflect.go
@@ -14,7 +14,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -26,7 +26,7 @@
 
 var ReflectTypeForAnalyzer = &analysis.Analyzer{
 	Name: "reflecttypefor",
-	Doc:  analysisinternal.MustExtractDoc(doc, "reflecttypefor"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "reflecttypefor"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/slices.go b/go/analysis/passes/modernize/slices.go
index f527d5d..f3c2ed2 100644
--- a/go/analysis/passes/modernize/slices.go
+++ b/go/analysis/passes/modernize/slices.go
@@ -14,7 +14,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/refactor"
@@ -25,7 +25,7 @@
 // Warning: this analyzer is not safe to enable by default.
 var AppendClippedAnalyzer = &analysis.Analyzer{
 	Name: "appendclipped",
-	Doc:  analysisinternal.MustExtractDoc(doc, "appendclipped"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "appendclipped"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/slicescontains.go b/go/analysis/passes/modernize/slicescontains.go
index 35d3c5d..097a9ff 100644
--- a/go/analysis/passes/modernize/slicescontains.go
+++ b/go/analysis/passes/modernize/slicescontains.go
@@ -14,7 +14,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -26,7 +26,7 @@
 
 var SlicesContainsAnalyzer = &analysis.Analyzer{
 	Name: "slicescontains",
-	Doc:  analysisinternal.MustExtractDoc(doc, "slicescontains"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "slicescontains"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/slicesdelete.go b/go/analysis/passes/modernize/slicesdelete.go
index ff38e2f..3860efe 100644
--- a/go/analysis/passes/modernize/slicesdelete.go
+++ b/go/analysis/passes/modernize/slicesdelete.go
@@ -12,7 +12,7 @@
 
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/refactor"
@@ -23,7 +23,7 @@
 // Warning: this analyzer is not safe to enable by default (not nil-preserving).
 var SlicesDeleteAnalyzer = &analysis.Analyzer{
 	Name: "slicesdelete",
-	Doc:  analysisinternal.MustExtractDoc(doc, "slicesdelete"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "slicesdelete"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/sortslice.go b/go/analysis/passes/modernize/sortslice.go
index 1dd2c5e..2d66d02 100644
--- a/go/analysis/passes/modernize/sortslice.go
+++ b/go/analysis/passes/modernize/sortslice.go
@@ -11,7 +11,7 @@
 
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -23,7 +23,7 @@
 // (Not to be confused with go/analysis/passes/sortslice.)
 var SlicesSortAnalyzer = &analysis.Analyzer{
 	Name: "slicessort",
-	Doc:  analysisinternal.MustExtractDoc(doc, "slicessort"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "slicessort"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/stditerators.go b/go/analysis/passes/modernize/stditerators.go
index 8f2ce70..e35bab8 100644
--- a/go/analysis/passes/modernize/stditerators.go
+++ b/go/analysis/passes/modernize/stditerators.go
@@ -14,7 +14,7 @@
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -26,7 +26,7 @@
 
 var stditeratorsAnalyzer = &analysis.Analyzer{
 	Name: "stditerators",
-	Doc:  analysisinternal.MustExtractDoc(doc, "stditerators"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "stditerators"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		typeindexanalyzer.Analyzer,
diff --git a/go/analysis/passes/modernize/stringsbuilder.go b/go/analysis/passes/modernize/stringsbuilder.go
index 56d0ba7..4fd9cf4 100644
--- a/go/analysis/passes/modernize/stringsbuilder.go
+++ b/go/analysis/passes/modernize/stringsbuilder.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -26,7 +26,7 @@
 
 var StringsBuilderAnalyzer = &analysis.Analyzer{
 	Name: "stringsbuilder",
-	Doc:  analysisinternal.MustExtractDoc(doc, "stringsbuilder"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "stringsbuilder"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/stringscut.go b/go/analysis/passes/modernize/stringscut.go
index 0cc575d..fdf47bb 100644
--- a/go/analysis/passes/modernize/stringscut.go
+++ b/go/analysis/passes/modernize/stringscut.go
@@ -18,7 +18,7 @@
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -31,7 +31,7 @@
 
 var stringscutAnalyzer = &analysis.Analyzer{
 	Name: "stringscut",
-	Doc:  analysisinternal.MustExtractDoc(doc, "stringscut"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "stringscut"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/stringscutprefix.go b/go/analysis/passes/modernize/stringscutprefix.go
index 113b76c..b76a36b 100644
--- a/go/analysis/passes/modernize/stringscutprefix.go
+++ b/go/analysis/passes/modernize/stringscutprefix.go
@@ -13,7 +13,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -25,7 +25,7 @@
 
 var StringsCutPrefixAnalyzer = &analysis.Analyzer{
 	Name: "stringscutprefix",
-	Doc:  analysisinternal.MustExtractDoc(doc, "stringscutprefix"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "stringscutprefix"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/stringsseq.go b/go/analysis/passes/modernize/stringsseq.go
index 9bacddb..96cffa6 100644
--- a/go/analysis/passes/modernize/stringsseq.go
+++ b/go/analysis/passes/modernize/stringsseq.go
@@ -14,7 +14,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/typesinternal/typeindex"
@@ -23,7 +23,7 @@
 
 var StringsSeqAnalyzer = &analysis.Analyzer{
 	Name: "stringsseq",
-	Doc:  analysisinternal.MustExtractDoc(doc, "stringsseq"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "stringsseq"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/testingcontext.go b/go/analysis/passes/modernize/testingcontext.go
index 63a7433..19cc825 100644
--- a/go/analysis/passes/modernize/testingcontext.go
+++ b/go/analysis/passes/modernize/testingcontext.go
@@ -17,7 +17,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -28,7 +28,7 @@
 
 var TestingContextAnalyzer = &analysis.Analyzer{
 	Name: "testingcontext",
-	Doc:  analysisinternal.MustExtractDoc(doc, "testingcontext"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "testingcontext"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/modernize/waitgroup.go b/go/analysis/passes/modernize/waitgroup.go
index 79c6939..2844ad9 100644
--- a/go/analysis/passes/modernize/waitgroup.go
+++ b/go/analysis/passes/modernize/waitgroup.go
@@ -14,7 +14,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/analysisinternal/generated"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
@@ -25,7 +25,7 @@
 
 var WaitGroupAnalyzer = &analysis.Analyzer{
 	Name: "waitgroup",
-	Doc:  analysisinternal.MustExtractDoc(doc, "waitgroup"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "waitgroup"),
 	Requires: []*analysis.Analyzer{
 		generated.Analyzer,
 		inspect.Analyzer,
diff --git a/go/analysis/passes/nilfunc/nilfunc.go b/go/analysis/passes/nilfunc/nilfunc.go
index 2b5a7c8..8e452f2 100644
--- a/go/analysis/passes/nilfunc/nilfunc.go
+++ b/go/analysis/passes/nilfunc/nilfunc.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
 
@@ -24,7 +24,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "nilfunc",
-	Doc:      analysisinternal.MustExtractDoc(doc, "nilfunc"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "nilfunc"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/nilfunc",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/nilness/nilness.go b/go/analysis/passes/nilness/nilness.go
index 4cd0b82..f24c4e3 100644
--- a/go/analysis/passes/nilness/nilness.go
+++ b/go/analysis/passes/nilness/nilness.go
@@ -13,7 +13,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/buildssa"
 	"golang.org/x/tools/go/ssa"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typeparams"
 )
 
@@ -22,7 +22,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "nilness",
-	Doc:      analysisinternal.MustExtractDoc(doc, "nilness"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "nilness"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/nilness",
 	Run:      run,
 	Requires: []*analysis.Analyzer{buildssa.Analyzer},
diff --git a/go/analysis/passes/printf/printf.go b/go/analysis/passes/printf/printf.go
index e3ee65d..55534ad 100644
--- a/go/analysis/passes/printf/printf.go
+++ b/go/analysis/passes/printf/printf.go
@@ -21,7 +21,7 @@
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/fmtstr"
 	"golang.org/x/tools/internal/typeparams"
@@ -38,7 +38,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:       "printf",
-	Doc:        analysisinternal.MustExtractDoc(doc, "printf"),
+	Doc:        analyzerutil.MustExtractDoc(doc, "printf"),
 	URL:        "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/printf",
 	Requires:   []*analysis.Analyzer{inspect.Analyzer},
 	Run:        run,
diff --git a/go/analysis/passes/reflectvaluecompare/reflectvaluecompare.go b/go/analysis/passes/reflectvaluecompare/reflectvaluecompare.go
index 5b64ec9..2f2a66d 100644
--- a/go/analysis/passes/reflectvaluecompare/reflectvaluecompare.go
+++ b/go/analysis/passes/reflectvaluecompare/reflectvaluecompare.go
@@ -13,7 +13,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
 
@@ -22,7 +22,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "reflectvaluecompare",
-	Doc:      analysisinternal.MustExtractDoc(doc, "reflectvaluecompare"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "reflectvaluecompare"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/reflectvaluecompare",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/shadow/shadow.go b/go/analysis/passes/shadow/shadow.go
index 499a868..781971c 100644
--- a/go/analysis/passes/shadow/shadow.go
+++ b/go/analysis/passes/shadow/shadow.go
@@ -13,7 +13,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 // NOTE: Experimental. Not part of the vet suite.
@@ -23,7 +23,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "shadow",
-	Doc:      analysisinternal.MustExtractDoc(doc, "shadow"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "shadow"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/shadow",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/sigchanyzer/sigchanyzer.go b/go/analysis/passes/sigchanyzer/sigchanyzer.go
index 934f391..08bcd8f 100644
--- a/go/analysis/passes/sigchanyzer/sigchanyzer.go
+++ b/go/analysis/passes/sigchanyzer/sigchanyzer.go
@@ -19,7 +19,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
 
@@ -29,7 +29,7 @@
 // Analyzer describes sigchanyzer analysis function detector.
 var Analyzer = &analysis.Analyzer{
 	Name:     "sigchanyzer",
-	Doc:      analysisinternal.MustExtractDoc(doc, "sigchanyzer"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "sigchanyzer"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/sigchanyzer",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/slog/slog.go b/go/analysis/passes/slog/slog.go
index 985be27..2f0758a 100644
--- a/go/analysis/passes/slog/slog.go
+++ b/go/analysis/passes/slog/slog.go
@@ -19,7 +19,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
@@ -29,7 +29,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "slog",
-	Doc:      analysisinternal.MustExtractDoc(doc, "slog"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "slog"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/slog",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/stdmethods/stdmethods.go b/go/analysis/passes/stdmethods/stdmethods.go
index c0ca2af..6206af3 100644
--- a/go/analysis/passes/stdmethods/stdmethods.go
+++ b/go/analysis/passes/stdmethods/stdmethods.go
@@ -13,7 +13,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 //go:embed doc.go
@@ -21,7 +21,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "stdmethods",
-	Doc:      analysisinternal.MustExtractDoc(doc, "stdmethods"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "stdmethods"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/stdmethods",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/stringintconv/string.go b/go/analysis/passes/stringintconv/string.go
index 19c72d2..5e263f1 100644
--- a/go/analysis/passes/stringintconv/string.go
+++ b/go/analysis/passes/stringintconv/string.go
@@ -14,7 +14,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/refactor"
 	"golang.org/x/tools/internal/typeparams"
 	"golang.org/x/tools/internal/typesinternal"
@@ -25,7 +25,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "stringintconv",
-	Doc:      analysisinternal.MustExtractDoc(doc, "stringintconv"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "stringintconv"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/stringintconv",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/testinggoroutine/testinggoroutine.go b/go/analysis/passes/testinggoroutine/testinggoroutine.go
index eba4e56..25d8545 100644
--- a/go/analysis/passes/testinggoroutine/testinggoroutine.go
+++ b/go/analysis/passes/testinggoroutine/testinggoroutine.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
 
@@ -30,7 +30,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "testinggoroutine",
-	Doc:      analysisinternal.MustExtractDoc(doc, "testinggoroutine"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "testinggoroutine"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/testinggoroutine",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/tests/tests.go b/go/analysis/passes/tests/tests.go
index 9a9a068..3b1580e 100644
--- a/go/analysis/passes/tests/tests.go
+++ b/go/analysis/passes/tests/tests.go
@@ -15,7 +15,7 @@
 	"unicode/utf8"
 
 	"golang.org/x/tools/go/analysis"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
@@ -25,7 +25,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name: "tests",
-	Doc:  analysisinternal.MustExtractDoc(doc, "tests"),
+	Doc:  analyzerutil.MustExtractDoc(doc, "tests"),
 	URL:  "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/tests",
 	Run:  run,
 }
diff --git a/go/analysis/passes/timeformat/timeformat.go b/go/analysis/passes/timeformat/timeformat.go
index 45b6822..1c58748 100644
--- a/go/analysis/passes/timeformat/timeformat.go
+++ b/go/analysis/passes/timeformat/timeformat.go
@@ -18,7 +18,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
 
@@ -30,7 +30,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "timeformat",
-	Doc:      analysisinternal.MustExtractDoc(doc, "timeformat"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "timeformat"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/timeformat",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/unmarshal/unmarshal.go b/go/analysis/passes/unmarshal/unmarshal.go
index d2d9b37..39ac4c0 100644
--- a/go/analysis/passes/unmarshal/unmarshal.go
+++ b/go/analysis/passes/unmarshal/unmarshal.go
@@ -13,7 +13,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
 
@@ -22,7 +22,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "unmarshal",
-	Doc:      analysisinternal.MustExtractDoc(doc, "unmarshal"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "unmarshal"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unmarshal",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/unreachable/unreachable.go b/go/analysis/passes/unreachable/unreachable.go
index 668a335..4b6865f 100644
--- a/go/analysis/passes/unreachable/unreachable.go
+++ b/go/analysis/passes/unreachable/unreachable.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/refactor"
 )
 
@@ -24,7 +24,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:             "unreachable",
-	Doc:              analysisinternal.MustExtractDoc(doc, "unreachable"),
+	Doc:              analyzerutil.MustExtractDoc(doc, "unreachable"),
 	URL:              "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unreachable",
 	Requires:         []*analysis.Analyzer{inspect.Analyzer},
 	RunDespiteErrors: true,
diff --git a/go/analysis/passes/unsafeptr/unsafeptr.go b/go/analysis/passes/unsafeptr/unsafeptr.go
index 24ff723..5ad4646 100644
--- a/go/analysis/passes/unsafeptr/unsafeptr.go
+++ b/go/analysis/passes/unsafeptr/unsafeptr.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
 
@@ -24,7 +24,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "unsafeptr",
-	Doc:      analysisinternal.MustExtractDoc(doc, "unsafeptr"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "unsafeptr"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unsafeptr",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/unusedresult/unusedresult.go b/go/analysis/passes/unusedresult/unusedresult.go
index d1fa81c..5375cc4 100644
--- a/go/analysis/passes/unusedresult/unusedresult.go
+++ b/go/analysis/passes/unusedresult/unusedresult.go
@@ -25,7 +25,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/astutil"
 )
 
@@ -34,7 +34,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "unusedresult",
-	Doc:      analysisinternal.MustExtractDoc(doc, "unusedresult"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "unusedresult"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unusedresult",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/unusedwrite/unusedwrite.go b/go/analysis/passes/unusedwrite/unusedwrite.go
index d3fff8b..a6a57b3 100644
--- a/go/analysis/passes/unusedwrite/unusedwrite.go
+++ b/go/analysis/passes/unusedwrite/unusedwrite.go
@@ -11,7 +11,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/buildssa"
 	"golang.org/x/tools/go/ssa"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typeparams"
 )
 
@@ -22,7 +22,7 @@
 // that are never read.
 var Analyzer = &analysis.Analyzer{
 	Name:     "unusedwrite",
-	Doc:      analysisinternal.MustExtractDoc(doc, "unusedwrite"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "unusedwrite"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/unusedwrite",
 	Requires: []*analysis.Analyzer{buildssa.Analyzer},
 	Run:      run,
diff --git a/go/analysis/passes/usesgenerics/usesgenerics.go b/go/analysis/passes/usesgenerics/usesgenerics.go
index f96cfec..247dbf9 100644
--- a/go/analysis/passes/usesgenerics/usesgenerics.go
+++ b/go/analysis/passes/usesgenerics/usesgenerics.go
@@ -11,7 +11,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typeparams/genericfeatures"
 )
 
@@ -20,7 +20,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:       "usesgenerics",
-	Doc:        analysisinternal.MustExtractDoc(doc, "usesgenerics"),
+	Doc:        analyzerutil.MustExtractDoc(doc, "usesgenerics"),
 	URL:        "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/usesgenerics",
 	Requires:   []*analysis.Analyzer{inspect.Analyzer},
 	Run:        run,
diff --git a/go/analysis/passes/waitgroup/waitgroup.go b/go/analysis/passes/waitgroup/waitgroup.go
index 88e4cc8..ef5eda2 100644
--- a/go/analysis/passes/waitgroup/waitgroup.go
+++ b/go/analysis/passes/waitgroup/waitgroup.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
 
@@ -24,7 +24,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "waitgroup",
-	Doc:      analysisinternal.MustExtractDoc(doc, "waitgroup"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "waitgroup"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/waitgroup",
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
diff --git a/go/analysis/unitchecker/unitchecker.go b/go/analysis/unitchecker/unitchecker.go
index 4d9d7424..410173b 100644
--- a/go/analysis/unitchecker/unitchecker.go
+++ b/go/analysis/unitchecker/unitchecker.go
@@ -49,7 +49,7 @@
 
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/internal/analysisflags"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/driverutil"
 	"golang.org/x/tools/internal/facts"
 )
 
@@ -183,9 +183,9 @@
 		// but apply all fixes from the root actions.
 
 		// Convert results to form needed by ApplyFixes.
-		fixActions := make([]analysisflags.FixAction, len(results))
+		fixActions := make([]driverutil.FixAction, len(results))
 		for i, res := range results {
-			fixActions[i] = analysisflags.FixAction{
+			fixActions[i] = driverutil.FixAction{
 				Name:         res.a.Name,
 				Pkg:          res.pkg,
 				FileSet:      fset,
@@ -193,7 +193,7 @@
 				Diagnostics:  res.diagnostics,
 			}
 		}
-		if err := analysisflags.ApplyFixes(fixActions, false); err != nil {
+		if err := driverutil.ApplyFixes(fixActions, analysisflags.Diff, false); err != nil {
 			// Fail when applying fixes failed.
 			log.Print(err)
 			exit = 1
@@ -210,7 +210,7 @@
 
 	if analysisflags.JSON {
 		// JSON output
-		tree := make(analysisflags.JSONTree)
+		tree := make(driverutil.JSONTree)
 		for _, res := range results {
 			tree.Add(fset, id, res.a.Name, res.diagnostics, res.err)
 		}
@@ -226,7 +226,7 @@
 		}
 		for _, res := range results {
 			for _, diag := range res.diagnostics {
-				analysisflags.PrintPlain(os.Stderr, fset, analysisflags.Context, diag)
+				driverutil.PrintPlain(os.Stderr, fset, analysisflags.Context, diag)
 				exit = 1
 			}
 		}
@@ -429,7 +429,7 @@
 				ResultOf:     inputs,
 				Report: func(d analysis.Diagnostic) {
 					// Unitchecker doesn't apply fixes, but it does report them in the JSON output.
-					if err := analysisinternal.ValidateFixes(fset, a, d.SuggestedFixes); err != nil {
+					if err := driverutil.ValidateFixes(fset, a, d.SuggestedFixes); err != nil {
 						// Since we have diagnostics, the exit code will be nonzero,
 						// so logging these errors is sufficient.
 						log.Println(err)
@@ -445,14 +445,14 @@
 				AllPackageFacts:   func() []analysis.PackageFact { return facts.AllPackageFacts(factFilter) },
 				Module:            module,
 			}
-			pass.ReadFile = analysisinternal.CheckedReadFile(pass, os.ReadFile)
+			pass.ReadFile = driverutil.CheckedReadFile(pass, os.ReadFile)
 
 			t0 := time.Now()
 			act.result, act.err = a.Run(pass)
 
 			if act.err == nil { // resolve URLs on diagnostics.
 				for i := range act.diagnostics {
-					if url, uerr := analysisflags.ResolveURL(a, act.diagnostics[i]); uerr == nil {
+					if url, uerr := driverutil.ResolveURL(a, act.diagnostics[i]); uerr == nil {
 						act.diagnostics[i].URL = url
 					} else {
 						act.err = uerr // keep the last error
diff --git a/gopls/internal/analysis/deprecated/deprecated.go b/gopls/internal/analysis/deprecated/deprecated.go
index 400041b..ab69598 100644
--- a/gopls/internal/analysis/deprecated/deprecated.go
+++ b/gopls/internal/analysis/deprecated/deprecated.go
@@ -18,7 +18,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	internalastutil "golang.org/x/tools/internal/astutil"
 )
 
@@ -27,7 +27,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:             "deprecated",
-	Doc:              analysisinternal.MustExtractDoc(doc, "deprecated"),
+	Doc:              analyzerutil.MustExtractDoc(doc, "deprecated"),
 	Requires:         []*analysis.Analyzer{inspect.Analyzer},
 	Run:              checkDeprecated,
 	FactTypes:        []analysis.Fact{(*deprecationFact)(nil)},
diff --git a/gopls/internal/analysis/embeddirective/embeddirective.go b/gopls/internal/analysis/embeddirective/embeddirective.go
index 2284055..744c368 100644
--- a/gopls/internal/analysis/embeddirective/embeddirective.go
+++ b/gopls/internal/analysis/embeddirective/embeddirective.go
@@ -12,7 +12,7 @@
 	"strings"
 
 	"golang.org/x/tools/go/analysis"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/refactor"
 )
 
@@ -21,7 +21,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:             "embed",
-	Doc:              analysisinternal.MustExtractDoc(doc, "embed"),
+	Doc:              analyzerutil.MustExtractDoc(doc, "embed"),
 	Run:              run,
 	RunDespiteErrors: true,
 	URL:              "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/embeddirective",
diff --git a/gopls/internal/analysis/fillreturns/fillreturns.go b/gopls/internal/analysis/fillreturns/fillreturns.go
index 411030a..2e68d44 100644
--- a/gopls/internal/analysis/fillreturns/fillreturns.go
+++ b/gopls/internal/analysis/fillreturns/fillreturns.go
@@ -21,7 +21,7 @@
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/gopls/internal/fuzzy"
 	"golang.org/x/tools/gopls/internal/util/cursorutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/moreiters"
 	"golang.org/x/tools/internal/typesinternal"
 )
@@ -31,7 +31,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:             "fillreturns",
-	Doc:              analysisinternal.MustExtractDoc(doc, "fillreturns"),
+	Doc:              analyzerutil.MustExtractDoc(doc, "fillreturns"),
 	Requires:         []*analysis.Analyzer{inspect.Analyzer},
 	Run:              run,
 	RunDespiteErrors: true,
diff --git a/gopls/internal/analysis/maprange/maprange.go b/gopls/internal/analysis/maprange/maprange.go
index 1b64e24..a8a6495 100644
--- a/gopls/internal/analysis/maprange/maprange.go
+++ b/gopls/internal/analysis/maprange/maprange.go
@@ -13,7 +13,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/packagepath"
@@ -27,7 +27,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "maprange",
-	Doc:      analysisinternal.MustExtractDoc(doc, "maprange"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "maprange"),
 	URL:      "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/maprange",
 	Requires: []*analysis.Analyzer{typeindexanalyzer.Analyzer},
 	Run:      run,
diff --git a/gopls/internal/analysis/nonewvars/nonewvars.go b/gopls/internal/analysis/nonewvars/nonewvars.go
index 86e13ed..a5d6cfd 100644
--- a/gopls/internal/analysis/nonewvars/nonewvars.go
+++ b/gopls/internal/analysis/nonewvars/nonewvars.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/gopls/internal/util/cursorutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
 
@@ -24,7 +24,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:             "nonewvars",
-	Doc:              analysisinternal.MustExtractDoc(doc, "nonewvars"),
+	Doc:              analyzerutil.MustExtractDoc(doc, "nonewvars"),
 	Requires:         []*analysis.Analyzer{inspect.Analyzer},
 	Run:              run,
 	RunDespiteErrors: true,
diff --git a/gopls/internal/analysis/noresultvalues/noresultvalues.go b/gopls/internal/analysis/noresultvalues/noresultvalues.go
index 92da081..43bfc00 100644
--- a/gopls/internal/analysis/noresultvalues/noresultvalues.go
+++ b/gopls/internal/analysis/noresultvalues/noresultvalues.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/gopls/internal/util/cursorutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
 
@@ -24,7 +24,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:             "noresultvalues",
-	Doc:              analysisinternal.MustExtractDoc(doc, "noresultvalues"),
+	Doc:              analyzerutil.MustExtractDoc(doc, "noresultvalues"),
 	Requires:         []*analysis.Analyzer{inspect.Analyzer},
 	Run:              run,
 	RunDespiteErrors: true,
diff --git a/gopls/internal/analysis/recursiveiter/recursiveiter.go b/gopls/internal/analysis/recursiveiter/recursiveiter.go
index 364855b..7f04fd8 100644
--- a/gopls/internal/analysis/recursiveiter/recursiveiter.go
+++ b/gopls/internal/analysis/recursiveiter/recursiveiter.go
@@ -14,7 +14,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/go/types/typeutil"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/typesinternal/typeindex"
 )
@@ -24,7 +24,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "recursiveiter",
-	Doc:      analysisinternal.MustExtractDoc(doc, "recursiveiter"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "recursiveiter"),
 	Requires: []*analysis.Analyzer{inspect.Analyzer, typeindexanalyzer.Analyzer},
 	Run:      run,
 	URL:      "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/recursiveiter",
diff --git a/gopls/internal/analysis/simplifycompositelit/simplifycompositelit.go b/gopls/internal/analysis/simplifycompositelit/simplifycompositelit.go
index a883922..d320279 100644
--- a/gopls/internal/analysis/simplifycompositelit/simplifycompositelit.go
+++ b/gopls/internal/analysis/simplifycompositelit/simplifycompositelit.go
@@ -19,7 +19,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 //go:embed doc.go
@@ -27,7 +27,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "simplifycompositelit",
-	Doc:      analysisinternal.MustExtractDoc(doc, "simplifycompositelit"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "simplifycompositelit"),
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
 	URL:      "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/simplifycompositelit",
diff --git a/gopls/internal/analysis/simplifyrange/simplifyrange.go b/gopls/internal/analysis/simplifyrange/simplifyrange.go
index 594ebd1..1ddfbd6 100644
--- a/gopls/internal/analysis/simplifyrange/simplifyrange.go
+++ b/gopls/internal/analysis/simplifyrange/simplifyrange.go
@@ -12,7 +12,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 //go:embed doc.go
@@ -20,7 +20,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "simplifyrange",
-	Doc:      analysisinternal.MustExtractDoc(doc, "simplifyrange"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "simplifyrange"),
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
 	URL:      "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/simplifyrange",
diff --git a/gopls/internal/analysis/simplifyslice/simplifyslice.go b/gopls/internal/analysis/simplifyslice/simplifyslice.go
index 8aae3c6..3db7d7c 100644
--- a/gopls/internal/analysis/simplifyslice/simplifyslice.go
+++ b/gopls/internal/analysis/simplifyslice/simplifyslice.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 //go:embed doc.go
@@ -23,7 +23,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "simplifyslice",
-	Doc:      analysisinternal.MustExtractDoc(doc, "simplifyslice"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "simplifyslice"),
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
 	URL:      "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/simplifyslice",
diff --git a/gopls/internal/analysis/unusedfunc/unusedfunc.go b/gopls/internal/analysis/unusedfunc/unusedfunc.go
index cf34337..9fe0fd6 100644
--- a/gopls/internal/analysis/unusedfunc/unusedfunc.go
+++ b/gopls/internal/analysis/unusedfunc/unusedfunc.go
@@ -16,7 +16,7 @@
 	"golang.org/x/tools/go/analysis/passes/inspect"
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/ast/inspector"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	typeindexanalyzer "golang.org/x/tools/internal/analysisinternal/typeindex"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/packagepath"
@@ -63,7 +63,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "unusedfunc",
-	Doc:      analysisinternal.MustExtractDoc(doc, "unusedfunc"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "unusedfunc"),
 	Requires: []*analysis.Analyzer{inspect.Analyzer, typeindexanalyzer.Analyzer},
 	Run:      run,
 	URL:      "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/unusedfunc",
diff --git a/gopls/internal/analysis/unusedparams/unusedparams.go b/gopls/internal/analysis/unusedparams/unusedparams.go
index 663c36f..300e4b2 100644
--- a/gopls/internal/analysis/unusedparams/unusedparams.go
+++ b/gopls/internal/analysis/unusedparams/unusedparams.go
@@ -15,7 +15,7 @@
 	"golang.org/x/tools/go/ast/edge"
 	"golang.org/x/tools/go/ast/inspector"
 	"golang.org/x/tools/gopls/internal/util/moreslices"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/typesinternal"
 )
@@ -25,7 +25,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "unusedparams",
-	Doc:      analysisinternal.MustExtractDoc(doc, "unusedparams"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "unusedparams"),
 	Requires: []*analysis.Analyzer{inspect.Analyzer},
 	Run:      run,
 	URL:      "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/unusedparams",
diff --git a/gopls/internal/analysis/yield/yield.go b/gopls/internal/analysis/yield/yield.go
index ef94d4d..d9c6ccb 100644
--- a/gopls/internal/analysis/yield/yield.go
+++ b/gopls/internal/analysis/yield/yield.go
@@ -32,7 +32,7 @@
 	"golang.org/x/tools/go/ssa"
 	"golang.org/x/tools/gopls/internal/util/cursorutil"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 //go:embed doc.go
@@ -40,7 +40,7 @@
 
 var Analyzer = &analysis.Analyzer{
 	Name:     "yield",
-	Doc:      analysisinternal.MustExtractDoc(doc, "yield"),
+	Doc:      analyzerutil.MustExtractDoc(doc, "yield"),
 	Requires: []*analysis.Analyzer{inspect.Analyzer, buildssa.Analyzer},
 	Run:      run,
 	URL:      "https://pkg.go.dev/golang.org/x/tools/gopls/internal/analysis/yield",
diff --git a/gopls/internal/cache/analysis.go b/gopls/internal/cache/analysis.go
index 31ce400..4ecadc6 100644
--- a/gopls/internal/cache/analysis.go
+++ b/gopls/internal/cache/analysis.go
@@ -44,7 +44,7 @@
 	"golang.org/x/tools/gopls/internal/util/moremaps"
 	"golang.org/x/tools/gopls/internal/util/persistent"
 	"golang.org/x/tools/gopls/internal/util/safetoken"
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/driverutil"
 	"golang.org/x/tools/internal/astutil"
 	"golang.org/x/tools/internal/event"
 	"golang.org/x/tools/internal/facts"
@@ -1036,7 +1036,7 @@
 			// ValidateFixes allows a fix.End to be slightly beyond
 			// EOF to avoid spurious assertions when reporting
 			// fixes as the end of truncated files; see #71659.
-			if err := analysisinternal.ValidateFixes(apkg.pkg.FileSet(), analyzer, d.SuggestedFixes); err != nil {
+			if err := driverutil.ValidateFixes(apkg.pkg.FileSet(), analyzer, d.SuggestedFixes); err != nil {
 				bug.Reportf("invalid SuggestedFixes: %v", err)
 				d.SuggestedFixes = nil
 			}
@@ -1067,7 +1067,7 @@
 		// hashing a potentially large number of mostly irrelevant
 		// files; or (b) some kind of dynamic dependency discovery
 		// system like used in Bazel for C++ headers. Neither entices.
-		if err := analysisinternal.CheckReadable(pass, filename); err != nil {
+		if err := driverutil.CheckReadable(pass, filename); err != nil {
 			return nil, err
 		}
 		h, err := act.fsource.ReadFile(ctx, protocol.URIFromPath(filename))
diff --git a/internal/analysisinternal/analyzerutil/doc.go b/internal/analysisinternal/analyzerutil/doc.go
new file mode 100644
index 0000000..74a2a1c
--- /dev/null
+++ b/internal/analysisinternal/analyzerutil/doc.go
@@ -0,0 +1,6 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package analyzerutil provides implementation helpers for analyzers.
+package analyzerutil
diff --git a/internal/analysisinternal/extractdoc.go b/internal/analysisinternal/analyzerutil/extractdoc.go
similarity index 96%
rename from internal/analysisinternal/extractdoc.go
rename to internal/analysisinternal/analyzerutil/extractdoc.go
index c6cdf59..772a030 100644
--- a/internal/analysisinternal/extractdoc.go
+++ b/internal/analysisinternal/analyzerutil/extractdoc.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package analysisinternal
+package analyzerutil
 
 import (
 	"fmt"
@@ -35,7 +35,7 @@
 //
 //	var Analyzer = &analysis.Analyzer{
 //		Name:             "halting",
-//		Doc:              analysisinternal.MustExtractDoc(doc, "halting"),
+//		Doc:              analyzerutil.MustExtractDoc(doc, "halting"),
 //		...
 //	}
 func MustExtractDoc(content, name string) string {
diff --git a/internal/analysisinternal/extractdoc_test.go b/internal/analysisinternal/analyzerutil/extractdoc_test.go
similarity index 91%
rename from internal/analysisinternal/extractdoc_test.go
rename to internal/analysisinternal/analyzerutil/extractdoc_test.go
index 8c99b11..4d6442e 100644
--- a/internal/analysisinternal/extractdoc_test.go
+++ b/internal/analysisinternal/analyzerutil/extractdoc_test.go
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package analysisinternal_test
+package analyzerutil_test
 
 import (
 	"testing"
 
-	"golang.org/x/tools/internal/analysisinternal"
+	"golang.org/x/tools/internal/analysisinternal/analyzerutil"
 )
 
 func TestExtractDoc(t *testing.T) {
@@ -68,7 +68,7 @@
 		{multi, "nocolon",
 			"error: 'Analyzer nocolon' heading not followed by 'nocolon: summary...' line"},
 	} {
-		got, err := analysisinternal.ExtractDoc(test.content, test.name)
+		got, err := analyzerutil.ExtractDoc(test.content, test.name)
 		if err != nil {
 			got = "error: " + err.Error()
 		}
diff --git a/internal/analysisinternal/analyzerutil/readfile.go b/internal/analysisinternal/analyzerutil/readfile.go
new file mode 100644
index 0000000..ecc30ca
--- /dev/null
+++ b/internal/analysisinternal/analyzerutil/readfile.go
@@ -0,0 +1,30 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package analyzerutil
+
+// This file defines helpers for calling [analysis.Pass.ReadFile].
+
+import (
+	"go/token"
+	"os"
+
+	"golang.org/x/tools/go/analysis"
+)
+
+// ReadFile reads a file and adds it to the FileSet in pass
+// so that we can report errors against it using lineStart.
+func ReadFile(pass *analysis.Pass, filename string) ([]byte, *token.File, error) {
+	readFile := pass.ReadFile
+	if readFile == nil {
+		readFile = os.ReadFile
+	}
+	content, err := readFile(filename)
+	if err != nil {
+		return nil, nil, err
+	}
+	tf := pass.Fset.AddFile(filename, -1, len(content))
+	tf.SetLinesForContent(content)
+	return content, tf, nil
+}
diff --git a/go/analysis/internal/analysisflags/fix.go b/internal/analysisinternal/driverutil/fix.go
similarity index 93%
rename from go/analysis/internal/analysisflags/fix.go
rename to internal/analysisinternal/driverutil/fix.go
index 44a4356..2b50ed1 100644
--- a/go/analysis/internal/analysisflags/fix.go
+++ b/internal/analysisinternal/driverutil/fix.go
@@ -2,17 +2,14 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package analysisflags
+// Package driverutil defines implementation helper functions for
+// analysis drivers such as unitchecker, {single,multi}checker, and
+// analysistest.
+package driverutil
 
 // This file defines the -fix logic common to unitchecker and
 // {single,multi}checker.
 
-// TODO(adonovan): move this file, and most of the contents of
-// internal/analysisinternal, into a new package, internal/driverlib,
-// since the library is for the driver, not analyzer, side of the
-// analysis API. Turn any dependencies on analysisflags state (i.e.
-// diffFlag) into explicit parameters (of ApplyFix).
-
 import (
 	"bytes"
 	"fmt"
@@ -29,7 +26,6 @@
 
 	"golang.org/x/tools/go/analysis"
 	"golang.org/x/tools/go/ast/astutil"
-	"golang.org/x/tools/internal/analysisinternal"
 	"golang.org/x/tools/internal/astutil/free"
 	"golang.org/x/tools/internal/diff"
 )
@@ -40,13 +36,13 @@
 	Name         string         // e.g. "analyzer@package"
 	Pkg          *types.Package // (for import removal)
 	FileSet      *token.FileSet
-	ReadFileFunc analysisinternal.ReadFileFunc
+	ReadFileFunc ReadFileFunc
 	Diagnostics  []analysis.Diagnostic
 }
 
 // ApplyFixes attempts to apply the first suggested fix associated
 // with each diagnostic reported by the specified actions.
-// All fixes must have been validated by [analysisinternal.ValidateFixes].
+// All fixes must have been validated by [ValidateFixes].
 //
 // Each fix is treated as an independent change; fixes are merged in
 // an arbitrary deterministic order as if by a three-way diff tool
@@ -94,12 +90,13 @@
 // applyFixes returns success if all fixes are valid, could be cleanly
 // merged, and the corresponding files were successfully updated.
 //
-// If the -diff flag was set, instead of updating the files it display the final
-// patch composed of all the cleanly merged fixes.
+// If printDiff (from the -diff flag) is set, instead of updating the
+// files it display the final patch composed of all the cleanly merged
+// fixes.
 //
 // TODO(adonovan): handle file-system level aliases such as symbolic
 // links using robustio.FileID.
-func ApplyFixes(actions []FixAction, verbose bool) error {
+func ApplyFixes(actions []FixAction, printDiff, verbose bool) error {
 	// Select fixes to apply.
 	//
 	// If there are several for a given Diagnostic, choose the first.
@@ -133,7 +130,7 @@
 	// packages are not disjoint, due to test variants, so this
 	// would not really address the issue.)
 	baselineContent := make(map[string][]byte)
-	getBaseline := func(readFile analysisinternal.ReadFileFunc, filename string) ([]byte, error) {
+	getBaseline := func(readFile ReadFileFunc, filename string) ([]byte, error) {
 		content, ok := baselineContent[filename]
 		if !ok {
 			var err error
@@ -237,7 +234,7 @@
 			final = formatted
 		}
 
-		if diffFlag {
+		if printDiff {
 			// Since we formatted the file, we need to recompute the diff.
 			unified := diff.Unified(file+" (old)", file+" (new)", string(baseline), string(final))
 			// TODO(adonovan): abstract the I/O.
@@ -287,7 +284,7 @@
 	//
 	// TODO(adonovan): should we log that n files were updated in case of total victory?
 	if badFixes > 0 || filesUpdated < totalFiles {
-		if diffFlag {
+		if printDiff {
 			return fmt.Errorf("%d of %d fixes skipped (e.g. due to conflicts)", badFixes, len(fixes))
 		} else {
 			return fmt.Errorf("applied %d of %d fixes; %d files updated. (Re-run the command to apply more.)",
diff --git a/internal/analysisinternal/driverutil/print.go b/internal/analysisinternal/driverutil/print.go
new file mode 100644
index 0000000..7fc42a5
--- /dev/null
+++ b/internal/analysisinternal/driverutil/print.go
@@ -0,0 +1,161 @@
+// Copyright 2025 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package driverutil
+
+// This file defined output helpers common to all drivers.
+
+import (
+	"encoding/json"
+	"fmt"
+	"go/token"
+	"io"
+	"log"
+	"os"
+	"strings"
+
+	"golang.org/x/tools/go/analysis"
+)
+
+// TODO(adonovan): don't accept an io.Writer if we don't report errors.
+// Either accept a bytes.Buffer (infallible), or return a []byte.
+
+// PrintPlain prints a diagnostic in plain text form.
+// If contextLines is nonnegative, it also prints the
+// offending line plus this many lines of context.
+func PrintPlain(out io.Writer, fset *token.FileSet, contextLines int, diag analysis.Diagnostic) {
+	print := func(pos, end token.Pos, message string) {
+		posn := fset.Position(pos)
+		fmt.Fprintf(out, "%s: %s\n", posn, message)
+
+		// show offending line plus N lines of context.
+		if contextLines >= 0 {
+			end := fset.Position(end)
+			if !end.IsValid() {
+				end = posn
+			}
+			// TODO(adonovan): highlight the portion of the line indicated
+			// by pos...end using ASCII art, terminal colors, etc?
+			data, _ := os.ReadFile(posn.Filename)
+			lines := strings.Split(string(data), "\n")
+			for i := posn.Line - contextLines; i <= end.Line+contextLines; i++ {
+				if 1 <= i && i <= len(lines) {
+					fmt.Fprintf(out, "%d\t%s\n", i, lines[i-1])
+				}
+			}
+		}
+	}
+
+	print(diag.Pos, diag.End, diag.Message)
+	for _, rel := range diag.Related {
+		print(rel.Pos, rel.End, "\t"+rel.Message)
+	}
+}
+
+// A JSONTree is a mapping from package ID to analysis name to result.
+// Each result is either a jsonError or a list of JSONDiagnostic.
+type JSONTree map[string]map[string]any
+
+// A TextEdit describes the replacement of a portion of a file.
+// Start and End are zero-based half-open indices into the original byte
+// sequence of the file, and New is the new text.
+type JSONTextEdit struct {
+	Filename string `json:"filename"`
+	Start    int    `json:"start"`
+	End      int    `json:"end"`
+	New      string `json:"new"`
+}
+
+// A JSONSuggestedFix describes an edit that should be applied as a whole or not
+// at all. It might contain multiple TextEdits/text_edits if the SuggestedFix
+// consists of multiple non-contiguous edits.
+type JSONSuggestedFix struct {
+	Message string         `json:"message"`
+	Edits   []JSONTextEdit `json:"edits"`
+}
+
+// A JSONDiagnostic describes the JSON schema of an analysis.Diagnostic.
+//
+// TODO(matloob): include End position if present.
+type JSONDiagnostic struct {
+	Category       string                   `json:"category,omitempty"`
+	Posn           string                   `json:"posn"` // e.g. "file.go:line:column"
+	Message        string                   `json:"message"`
+	SuggestedFixes []JSONSuggestedFix       `json:"suggested_fixes,omitempty"`
+	Related        []JSONRelatedInformation `json:"related,omitempty"`
+}
+
+// A JSONRelated describes a secondary position and message related to
+// a primary diagnostic.
+//
+// TODO(adonovan): include End position if present.
+type JSONRelatedInformation struct {
+	Posn    string `json:"posn"` // e.g. "file.go:line:column"
+	Message string `json:"message"`
+}
+
+// Add adds the result of analysis 'name' on package 'id'.
+// The result is either a list of diagnostics or an error.
+func (tree JSONTree) Add(fset *token.FileSet, id, name string, diags []analysis.Diagnostic, err error) {
+	var v any
+	if err != nil {
+		type jsonError struct {
+			Err string `json:"error"`
+		}
+		v = jsonError{err.Error()}
+	} else if len(diags) > 0 {
+		diagnostics := make([]JSONDiagnostic, 0, len(diags))
+		for _, f := range diags {
+			var fixes []JSONSuggestedFix
+			for _, fix := range f.SuggestedFixes {
+				var edits []JSONTextEdit
+				for _, edit := range fix.TextEdits {
+					edits = append(edits, JSONTextEdit{
+						Filename: fset.Position(edit.Pos).Filename,
+						Start:    fset.Position(edit.Pos).Offset,
+						End:      fset.Position(edit.End).Offset,
+						New:      string(edit.NewText),
+					})
+				}
+				fixes = append(fixes, JSONSuggestedFix{
+					Message: fix.Message,
+					Edits:   edits,
+				})
+			}
+			var related []JSONRelatedInformation
+			for _, r := range f.Related {
+				related = append(related, JSONRelatedInformation{
+					Posn:    fset.Position(r.Pos).String(),
+					Message: r.Message,
+				})
+			}
+			jdiag := JSONDiagnostic{
+				Category:       f.Category,
+				Posn:           fset.Position(f.Pos).String(),
+				Message:        f.Message,
+				SuggestedFixes: fixes,
+				Related:        related,
+			}
+			diagnostics = append(diagnostics, jdiag)
+		}
+		v = diagnostics
+	}
+	if v != nil {
+		m, ok := tree[id]
+		if !ok {
+			m = make(map[string]any)
+			tree[id] = m
+		}
+		m[name] = v
+	}
+}
+
+func (tree JSONTree) Print(out io.Writer) error {
+	data, err := json.MarshalIndent(tree, "", "\t")
+	if err != nil {
+		log.Panicf("internal error: JSON marshaling failed: %v", err)
+	}
+	_, err = fmt.Fprintf(out, "%s\n", data)
+	return err
+}
diff --git a/internal/analysisinternal/driverutil/readfile.go b/internal/analysisinternal/driverutil/readfile.go
new file mode 100644
index 0000000..dc1d54d
--- /dev/null
+++ b/internal/analysisinternal/driverutil/readfile.go
@@ -0,0 +1,43 @@
+// Copyright 2020 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package driverutil
+
+// This file defines helpers for implementing [analysis.Pass.ReadFile].
+
+import (
+	"fmt"
+	"slices"
+
+	"golang.org/x/tools/go/analysis"
+)
+
+// A ReadFileFunc is a function that returns the
+// contents of a file, such as [os.ReadFile].
+type ReadFileFunc = func(filename string) ([]byte, error)
+
+// CheckedReadFile returns a wrapper around a Pass.ReadFile
+// function that performs the appropriate checks.
+func CheckedReadFile(pass *analysis.Pass, readFile ReadFileFunc) ReadFileFunc {
+	return func(filename string) ([]byte, error) {
+		if err := CheckReadable(pass, filename); err != nil {
+			return nil, err
+		}
+		return readFile(filename)
+	}
+}
+
+// CheckReadable enforces the access policy defined by the ReadFile field of [analysis.Pass].
+func CheckReadable(pass *analysis.Pass, filename string) error {
+	if slices.Contains(pass.OtherFiles, filename) ||
+		slices.Contains(pass.IgnoredFiles, filename) {
+		return nil
+	}
+	for _, f := range pass.Files {
+		if pass.Fset.File(f.FileStart).Name() == filename {
+			return nil
+		}
+	}
+	return fmt.Errorf("Pass.ReadFile: %s is not among OtherFiles, IgnoredFiles, or names of Files", filename)
+}
diff --git a/go/analysis/internal/analysisflags/url.go b/internal/analysisinternal/driverutil/url.go
similarity index 97%
rename from go/analysis/internal/analysisflags/url.go
rename to internal/analysisinternal/driverutil/url.go
index 26a917a..93b3ecf 100644
--- a/go/analysis/internal/analysisflags/url.go
+++ b/internal/analysisinternal/driverutil/url.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package analysisflags
+package driverutil
 
 import (
 	"fmt"
diff --git a/go/analysis/internal/analysisflags/url_test.go b/internal/analysisinternal/driverutil/url_test.go
similarity index 92%
rename from go/analysis/internal/analysisflags/url_test.go
rename to internal/analysisinternal/driverutil/url_test.go
index 23876a8..318fb17 100644
--- a/go/analysis/internal/analysisflags/url_test.go
+++ b/internal/analysisinternal/driverutil/url_test.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package analysisflags_test
+package driverutil_test
 
 import (
 	"fmt"
@@ -10,7 +10,7 @@
 	"testing"
 
 	"golang.org/x/tools/go/analysis"
-	"golang.org/x/tools/go/analysis/internal/analysisflags"
+	"golang.org/x/tools/internal/analysisinternal/driverutil"
 )
 
 func TestResolveURLs(t *testing.T) {
@@ -39,7 +39,7 @@
 		{aURL, analysis.Diagnostic{Category: "category", URL: "https://absolute.diagnostic"}, "https://absolute.diagnostic"},
 	}
 	for _, c := range tests {
-		got, err := analysisflags.ResolveURL(c.analyzer, c.diagnostic)
+		got, err := driverutil.ResolveURL(c.analyzer, c.diagnostic)
 		if err != nil {
 			t.Errorf("Unexpected error from ResolveURL %s", err)
 		} else if got != c.want {
@@ -58,7 +58,7 @@
 		{&analysis.Analyzer{URL: "https://analyzer.example"}, analysis.Diagnostic{Category: "", URL: ":not a URL"}, "invalid Diagnostic.URL"},
 	}
 	for _, c := range tests {
-		_, err := analysisflags.ResolveURL(c.analyzer, c.diagnostic)
+		_, err := driverutil.ResolveURL(c.analyzer, c.diagnostic)
 		if got := fmt.Sprint(err); !strings.HasPrefix(got, c.want) {
 			t.Errorf("ResolveURL(%q, %q) expected an error starting with %q. got %q", c.analyzer.URL, c.diagnostic.URL, c.want, got)
 		}
diff --git a/internal/analysisinternal/analysis.go b/internal/analysisinternal/driverutil/validatefix.go
similarity index 65%
rename from internal/analysisinternal/analysis.go
rename to internal/analysisinternal/driverutil/validatefix.go
index ce296b5..7efc419 100644
--- a/internal/analysisinternal/analysis.go
+++ b/internal/analysisinternal/driverutil/validatefix.go
@@ -1,71 +1,20 @@
-// Copyright 2020 The Go Authors. All rights reserved.
+// Copyright 2025 The Go Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package analysisinternal provides helper functions for use in both
-// the analysis drivers in go/analysis and gopls, and in various
-// analyzers.
-//
-// TODO(adonovan): this is not ideal as it may lead to unnecessary
-// dependencies between drivers and analyzers. Split into analyzerlib
-// and driverlib?
-package analysisinternal
+package driverutil
+
+// This file defines the validation of SuggestedFixes.
 
 import (
 	"cmp"
 	"fmt"
 	"go/token"
-	"os"
 	"slices"
 
 	"golang.org/x/tools/go/analysis"
 )
 
-// ReadFile reads a file and adds it to the FileSet in pass
-// so that we can report errors against it using lineStart.
-func ReadFile(pass *analysis.Pass, filename string) ([]byte, *token.File, error) {
-	readFile := pass.ReadFile
-	if readFile == nil {
-		readFile = os.ReadFile
-	}
-	content, err := readFile(filename)
-	if err != nil {
-		return nil, nil, err
-	}
-	tf := pass.Fset.AddFile(filename, -1, len(content))
-	tf.SetLinesForContent(content)
-	return content, tf, nil
-}
-
-// A ReadFileFunc is a function that returns the
-// contents of a file, such as [os.ReadFile].
-type ReadFileFunc = func(filename string) ([]byte, error)
-
-// CheckedReadFile returns a wrapper around a Pass.ReadFile
-// function that performs the appropriate checks.
-func CheckedReadFile(pass *analysis.Pass, readFile ReadFileFunc) ReadFileFunc {
-	return func(filename string) ([]byte, error) {
-		if err := CheckReadable(pass, filename); err != nil {
-			return nil, err
-		}
-		return readFile(filename)
-	}
-}
-
-// CheckReadable enforces the access policy defined by the ReadFile field of [analysis.Pass].
-func CheckReadable(pass *analysis.Pass, filename string) error {
-	if slices.Contains(pass.OtherFiles, filename) ||
-		slices.Contains(pass.IgnoredFiles, filename) {
-		return nil
-	}
-	for _, f := range pass.Files {
-		if pass.Fset.File(f.FileStart).Name() == filename {
-			return nil
-		}
-	}
-	return fmt.Errorf("Pass.ReadFile: %s is not among OtherFiles, IgnoredFiles, or names of Files", filename)
-}
-
 // ValidateFixes validates the set of fixes for a single diagnostic.
 // Any error indicates a bug in the originating analyzer.
 //