1package main23import (4 "strconv"5 "strings"67 "golang.org/x/tools/go/analysis"8)910// edgeRule decides one import edge: the importing lane, the importing11// package's module-relative path, and the imported path in. The empty string12// means the edge is permitted.13type edgeRule func(from Lane, fromRel, importPath string) string1415// runImportRule adapts an edgeRule to the analysis framework. Every import16// rule shares this walk so a new boundary is a function, not a driver.17func runImportRule(rule edgeRule) func(*analysis.Pass) (any, error) {18 return func(pass *analysis.Pass) (any, error) {19 if testBinary(pass) {20 return nil, nil21 }22 fromRel, ok := rel(packagePath(pass))23 if !ok {24 return nil, nil25 }26 from := laneOf(fromRel)27 if from == LaneOutside {28 return nil, nil29 }3031 r := newReporter(pass)32 r.checkPragmas()33 for _, f := range pass.Files {34 for _, spec := range f.Imports {35 path, err := strconv.Unquote(spec.Path.Value)36 if err != nil {37 continue38 }39 if msg := rule(from, fromRel, path); msg != "" {40 r.reportf(importPos(spec), msg)41 }42 }43 }44 return nil, nil45 }46}4748// packagePath normalizes the path of a package under analysis. Test variants49// arrive as `example.com/p [example.com/p.test]`; the rules care about the50// package, not the variant.51func packagePath(pass *analysis.Pass) string {52 path := pass.Pkg.Path()53 if i := strings.Index(path, " ["); i >= 0 {54 path = path[:i]55 }56 return strings.TrimSuffix(path, "_test")57}5859// testBinary reports whether a pass is over the package `go test` synthesizes60// to run a test binary. It is generated code -- a main that imports os and61// testing and calls into the package under test -- and it carries the import62// path of the package it was written for with a .test suffix, which would63// otherwise read as that package importing os. Nobody wrote it; judging it64// says nothing about anybody's boundaries.65func testBinary(pass *analysis.Pass) bool {66 return strings.HasSuffix(pass.Pkg.Path(), ".test")67}