// 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 packagepath provides metadata operations on package path // strings. package packagepath // (This package should not depend on go/ast.) import "strings" // CanImport reports whether one package is allowed to import another. // // TODO(adonovan): allow customization of the accessibility relation // (e.g. for Bazel). func CanImport(from, to string) bool { // TODO(adonovan): better segment hygiene. if to == "internal" || strings.HasPrefix(to, "internal/") { // Special case: only std packages may import internal/... // We can't reliably know whether we're in std, so we // use a heuristic on the first segment. first, _, _ := strings.Cut(from, "/") if strings.Contains(first, ".") { return false // example.com/foo ∉ std } if first == "testdata" { return false // testdata/foo ∉ std } } if strings.HasSuffix(to, "/internal") { return strings.HasPrefix(from, to[:len(to)-len("/internal")]) } if i := strings.LastIndex(to, "/internal/"); i >= 0 { return strings.HasPrefix(from, to[:i]) } return true } // IsStdPackage reports whether the specified package path belongs to a // package in the standard library (including internal dependencies). func IsStdPackage(path string) bool { // A standard package has no dot in its first segment. // (It may yet have a dot, e.g. "vendor/golang.org/x/foo".) slash := strings.IndexByte(path, '/') if slash < 0 { slash = len(path) } return !strings.Contains(path[:slash], ".") && path != "testdata" }