gotools/go/analysis/analysistest/analysistest.go

// WriteFiles is a helper function that creates a temporary directory
// and populates it with a GOPATH-style project using filemap (which
// maps file names to contents). On success it returns the name of the
// directory and a cleanup function to delete it.
func WriteFiles(filemap map[string]string) (dir string, cleanup func(), err error) {}

var TestData

type Testing

// RunWithSuggestedFixes behaves like Run, but additionally verifies suggested fixes.
// It uses golden files placed alongside the source code under analysis:
// suggested fixes for code in example.go will be compared against example.go.golden.
//
// Golden files can be formatted in one of two ways: as plain Go source code, or as txtar archives.
// In the first case, all suggested fixes will be applied to the original source, which will then be compared against the golden file.
// In the second case, suggested fixes will be grouped by their messages, and each set of fixes will be applied and tested separately.
// Each section in the archive corresponds to a single message.
//
// A golden file using txtar may look like this:
//
//	-- turn into single negation --
//	package pkg
//
//	func fn(b1, b2 bool) {
//		if !b1 { // want `negating a boolean twice`
//			println()
//		}
//	}
//
//	-- remove double negation --
//	package pkg
//
//	func fn(b1, b2 bool) {
//		if b1 { // want `negating a boolean twice`
//			println()
//		}
//	}
//
// # Conflicts
//
// A single analysis pass may offer two or more suggested fixes that
// (1) conflict but are nonetheless logically composable, (e.g.
// because both update the import declaration), or (2) are
// fundamentally incompatible (e.g. alternative fixes to the same
// statement).
//
// It is up to the driver to decide how to apply such fixes. A
// sophisticated driver could attempt to resolve conflicts of the
// first kind, but this test driver simply reports the fact of the
// conflict with the expectation that the user will split their tests
// into nonconflicting parts.
//
// Conflicts of the second kind can be avoided by giving the
// alternative fixes different names (SuggestedFix.Message) and
// defining the .golden file as a multi-section txtar file with a
// named section for each alternative fix, as shown above.
//
// Analyzers that compute fixes from a textual diff of the
// before/after file contents (instead of directly from syntax tree
// positions) may produce fixes that, although logically
// non-conflicting, nonetheless conflict due to the particulars of the
// diff algorithm. In such cases it may suffice to introduce
// sufficient separation of the statements in the test input so that
// the computed diffs do not overlap. If that fails, break the test
// into smaller parts.
//
// TODO(adonovan): the behavior of RunWithSuggestedFixes as documented
// above is impractical for tests that report multiple diagnostics and
// offer multiple alternative fixes for the same diagnostic, and it is
// inconsistent with the interpretation of multiple diagnostics
// described at Diagnostic.SuggestedFixes.
// We need to rethink the analyzer testing API to better support such
// cases. In the meantime, users of RunWithSuggestedFixes testing
// analyzers that offer alternative fixes are advised to put each fix
// in a separate .go file in the testdata.
func RunWithSuggestedFixes(t Testing, dir string, a *analysis.Analyzer, patterns ...string) []*Result {}

// applyDiffsAndCompare applies edits to src and compares the results against
// golden after formatting both. fileName is use solely for error reporting.
func applyDiffsAndCompare(src, golden []byte, edits []diff.Edit, fileName string) error {}

// Run applies an analysis to the packages denoted by the "go list" patterns.
//
// It loads the packages from the specified
// directory using golang.org/x/tools/go/packages, runs the analysis on
// them, and checks that each analysis emits the expected diagnostics
// and facts specified by the contents of '// want ...' comments in the
// package's source files. It treats a comment of the form
// "//...// want..." or "/*...// want... */" as if it starts at 'want'.
//
// If the directory contains a go.mod file, Run treats it as the root of the
// Go module in which to work. Otherwise, Run treats it as the root of a
// GOPATH-style tree, with package contained in the src subdirectory.
//
// An expectation of a Diagnostic is specified by a string literal
// containing a regular expression that must match the diagnostic
// message. For example:
//
//	fmt.Printf("%s", 1) // want `cannot provide int 1 to %s`
//
// An expectation of a Fact associated with an object is specified by
// 'name:"pattern"', where name is the name of the object, which must be
// declared on the same line as the comment, and pattern is a regular
// expression that must match the string representation of the fact,
// fmt.Sprint(fact). For example:
//
//	func panicf(format string, args interface{}) { // want panicf:"printfWrapper"
//
// Package facts are specified by the name "package" and appear on
// line 1 of the first source file of the package.
//
// A single 'want' comment may contain a mixture of diagnostic and fact
// expectations, including multiple facts about the same object:
//
//	// want "diag" "diag2" x:"fact1" x:"fact2" y:"fact3"
//
// Unexpected diagnostics and facts, and unmatched expectations, are
// reported as errors to the Testing.
//
// Run reports an error to the Testing if loading or analysis failed.
// Run also returns a Result for each package for which analysis was
// attempted, even if unsuccessful. It is safe for a test to ignore all
// the results, but a test may use it to perform additional checks.
func Run(t Testing, dir string, a *analysis.Analyzer, patterns ...string) []*Result {}

type Result

// loadPackages uses go/packages to load a specified packages (from source, with
// dependencies) from dir, which is the root of a GOPATH-style project tree.
// loadPackages returns an error if any package had an error, or the pattern
// matched no packages.
func loadPackages(a *analysis.Analyzer, dir string, patterns ...string) ([]*packages.Package, error) {}

// check inspects an analysis pass on which the analysis has already
// been run, and verifies that all reported diagnostics and facts match
// specified by the contents of "// want ..." comments in the package's
// source files, which must have been parsed with comments enabled.
func check(t Testing, gopath string, pass *analysis.Pass, diagnostics []analysis.Diagnostic, facts map[types.Object][]analysis.Fact) {}

type expectation

func (ex expectation) String() string {}

// parseExpectations parses the content of a "// want ..." comment
// and returns the expectations, a mixture of diagnostics ("rx") and
// facts (name:"rx").
func parseExpectations(text string) (lineDelta int, expects []expectation, err error) {}

// sanitize removes the GOPATH portion of the filename,
// typically a gnarly /tmp directory, and returns the rest.
func sanitize(gopath, filename string) string {}