aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorEthan Lee <ethanalee@google.com>2026-03-25 18:18:15 +0000
committerGopher Robot <gobot@golang.org>2026-03-26 14:02:59 -0700
commit34edebc0803b121acd1cbf363eef074e0a13ab6d (patch)
tree72af43d88fd8e6c83d93beae0dba3cc62395e70d /internal
parent8378ff811c1228f6a50808cb7557e6c08782205a (diff)
downloadgo-x-pkgsite-34edebc0803b121acd1cbf363eef074e0a13ab6d.tar.xz
internal: consolidate build context and unit resolution logic
- Centralize logic for resolving units and selecting the best matching build context. - New helpers MatchingBuildContext, SortedBuildContexts provide a consistent, efficient approach to build context selection. - DocumentationForBuildContext now finds the best match based on precdence rules rather than just returning the first match. - A new getUnitContext helper resolves unit metadata and the best build context in a single query. - GetSymbols and GetUnit are now both optimized to use pre-resolved IDs, simplifying their queries and removing redundant joins. - Replaced redundant matchingDoc functions in fetchdatasource and fakedatasource. Change-Id: I9207d3bfe03404483c816090a0b99666f14a36e3 Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/759162 Reviewed-by: Jonathan Amsterdam <jba@google.com> kokoro-CI: kokoro <noreply+kokoro@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Ethan Lee <ethanalee@google.com>
Diffstat (limited to 'internal')
-rw-r--r--internal/build_context.go58
-rw-r--r--internal/fetch/unit.go6
-rw-r--r--internal/fetchdatasource/fetchdatasource.go21
-rw-r--r--internal/postgres/package_symbol.go104
-rw-r--r--internal/postgres/unit.go153
-rw-r--r--internal/testing/fakedatasource/fakedatasource.go34
6 files changed, 205 insertions, 171 deletions
diff --git a/internal/build_context.go b/internal/build_context.go
index 63b1b77e..91c051b4 100644
--- a/internal/build_context.go
+++ b/internal/build_context.go
@@ -4,7 +4,10 @@
package internal
-import "fmt"
+import (
+ "fmt"
+ "slices"
+)
// A BuildContext describes a build context for the Go tool: information needed
// to build a Go package. For our purposes, we only care about the information
@@ -41,7 +44,11 @@ var (
// BuildContexts are the build contexts we check when loading a package (see
// internal/fetch/load.go).
// We store documentation for all of the listed contexts.
-// The order determines which environment's docs we will show as the default.
+//
+// The order of this slice determines the precedence when multiple build
+// contexts match a request. For example, if a user provides no GOOS or GOARCH,
+// the first context in this list that is available in the database will be
+// chosen as the default (typically linux/amd64).
var BuildContexts = []BuildContext{
BuildContextLinux,
BuildContextWindows,
@@ -82,17 +89,46 @@ func (d *Documentation) BuildContext() BuildContext {
return BuildContext{GOOS: d.GOOS, GOARCH: d.GOARCH}
}
-// DocumentationForBuildContext returns the first Documentation the list that
-// matches the BuildContext, or nil if none does. A Documentation matches if its
-// GOOS and GOARCH fields are the same as those of the BuildContext, or if the
-// Documentation field is "all", or if the BuildContext field is empty. That is,
-// empty BuildContext fields act as wildcards. So the zero BuildContext will
-// match the first element of docs, if there is one.
+// DocumentationForBuildContext returns the Documentation from the list that
+// matches the BuildContext and has the highest precedence according to
+// CompareBuildContexts. It returns nil if no match is found.
+// Empty BuildContext fields act as wildcards.
func DocumentationForBuildContext(docs []*Documentation, bc BuildContext) *Documentation {
+ var (
+ bestDoc *Documentation
+ bestBC BuildContext
+ )
for _, d := range docs {
- if bc.Match(BuildContext{d.GOOS, d.GOARCH}) {
- return d
+ dbc := d.BuildContext()
+ if bc.Match(dbc) && (bestDoc == nil || CompareBuildContexts(dbc, bestBC) < 0) {
+ bestDoc = d
+ bestBC = dbc
+ }
+ }
+ return bestDoc
+}
+
+// MatchingBuildContext returns the best BuildContext from the given bcs that matches bc,
+// according to CompareBuildContexts. It returns zero BuildContext and false if no match is
+// found.
+func MatchingBuildContext(bcs []BuildContext, bc BuildContext) (BuildContext, bool) {
+ var best BuildContext
+ found := false
+ for _, c := range bcs {
+ if bc.Match(c) {
+ if !found || CompareBuildContexts(c, best) < 0 {
+ best = c
+ found = true
+ }
}
}
- return nil
+ return best, found
+}
+
+// SortedBuildContexts returns a copy of the given slice of BuildContexts, sorted by
+// precedence according to CompareBuildContexts.
+func SortedBuildContexts(bcs []BuildContext) []BuildContext {
+ sorted := slices.Clone(bcs)
+ slices.SortFunc(sorted, CompareBuildContexts)
+ return sorted
}
diff --git a/internal/fetch/unit.go b/internal/fetch/unit.go
index e0c9c6f3..301d2b7d 100644
--- a/internal/fetch/unit.go
+++ b/internal/fetch/unit.go
@@ -6,7 +6,6 @@ package fetch
import (
"path"
- "sort"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/licenses"
@@ -45,10 +44,7 @@ func moduleUnit(modulePath string, unitMeta *internal.UnitMeta,
for _, d := range unit.Documentation {
bcs = append(bcs, internal.BuildContext{GOOS: d.GOOS, GOARCH: d.GOARCH})
}
- sort.Slice(bcs, func(i, j int) bool {
- return internal.CompareBuildContexts(bcs[i], bcs[j]) < 0
- })
- unit.BuildContexts = bcs
+ unit.BuildContexts = internal.SortedBuildContexts(bcs)
}
return unit
}
diff --git a/internal/fetchdatasource/fetchdatasource.go b/internal/fetchdatasource/fetchdatasource.go
index 7a5f5669..dfe30fc9 100644
--- a/internal/fetchdatasource/fetchdatasource.go
+++ b/internal/fetchdatasource/fetchdatasource.go
@@ -220,7 +220,7 @@ func (ds *FetchDataSource) GetUnit(ctx context.Context, um *internal.UnitMeta, f
// Since we cache the module and its units, we have to copy this unit before we modify it.
// It can be a shallow copy, since we're only modifying the Unit.Documentation field.
u2 := *u
- if d := matchingDoc(u.Documentation, bc); d != nil {
+ if d := internal.DocumentationForBuildContext(u.Documentation, bc); d != nil {
u2.Documentation = []*internal.Documentation{d}
} else {
u2.Documentation = nil
@@ -252,23 +252,6 @@ func findUnitMeta(m *fetch.LazyModule, path string) (*internal.UnitMeta, error)
return nil, derrors.NotFound
}
-// matchingDoc returns the Documentation that matches the given build context
-// and comes earliest in build-context order. It returns nil if there is none.
-func matchingDoc(docs []*internal.Documentation, bc internal.BuildContext) *internal.Documentation {
- var (
- dMin *internal.Documentation
- bcMin = internal.BuildContext{GOOS: "unk", GOARCH: "unk"} // sorts last
- )
- for _, d := range docs {
- dbc := d.BuildContext()
- if bc.Match(dbc) && internal.CompareBuildContexts(dbc, bcMin) < 0 {
- dMin = d
- bcMin = dbc
- }
- }
- return dMin
-}
-
// GetLatestInfo returns latest information for unitPath and modulePath.
func (ds *FetchDataSource) GetLatestInfo(ctx context.Context, unitPath, modulePath string, latestUnitMeta *internal.UnitMeta) (latest internal.LatestInfo, err error) {
defer derrors.Wrap(&err, "FetchDataSource.GetLatestInfo(ctx, %q, %q)", unitPath, modulePath)
@@ -447,7 +430,7 @@ func (ds *FetchDataSource) GetSymbols(ctx context.Context, pkgPath, modulePath,
return nil, err
}
- doc := matchingDoc(unit.Documentation, bc)
+ doc := internal.DocumentationForBuildContext(unit.Documentation, bc)
if doc == nil || len(doc.API) == 0 {
return nil, derrors.NotFound
}
diff --git a/internal/postgres/package_symbol.go b/internal/postgres/package_symbol.go
index 4d99b07c..d77b60a6 100644
--- a/internal/postgres/package_symbol.go
+++ b/internal/postgres/package_symbol.go
@@ -8,7 +8,6 @@ import (
"context"
"database/sql"
"fmt"
- "sort"
"github.com/Masterminds/squirrel"
"golang.org/x/pkgsite/internal"
@@ -22,16 +21,22 @@ func (db *DB) GetSymbols(ctx context.Context, pkgPath, modulePath, version strin
defer derrors.Wrap(&err, "DB.GetSymbols(ctx, %q, %q, %q, %v)", pkgPath, modulePath, version, bc)
defer stats.Elapsed(ctx, "DB.GetSymbols")()
- query := packageSymbolQueryJoin(
+ uc, err := db.getUnitContext(ctx, pkgPath, modulePath, version, bc)
+ if err != nil {
+ return nil, err
+ }
+ if uc.docID == 0 {
+ return nil, derrors.NotFound
+ }
+
+ query := packageSymbolQuery(
squirrel.Select(
"s1.name AS symbol_name",
"s2.name AS parent_symbol_name",
"ps.section",
"ps.type",
- "ps.synopsis",
- "d.goos",
- "d.goarch"), pkgPath, modulePath).
- Where(squirrel.Eq{"m.version": version}).
+ "ps.synopsis")).
+ Where(squirrel.Eq{"ds.documentation_id": uc.docID}).
OrderBy("CASE WHEN ps.type='Type' THEN 0 ELSE 1 END").
OrderBy("s1.name")
@@ -40,66 +45,47 @@ func (db *DB) GetSymbols(ctx context.Context, pkgPath, modulePath, version strin
return nil, err
}
- resultsByBC := make(map[internal.BuildContext][]internal.SymbolMeta)
+ var symbols []*internal.Symbol
+ symbolMap := make(map[string]*internal.Symbol)
collect := func(rows *sql.Rows) error {
var (
- name, parentName, synopsis, goos, goarch string
- section internal.SymbolSection
- kind internal.SymbolKind
+ name, parentName, synopsis string
+ section internal.SymbolSection
+ kind internal.SymbolKind
)
- if err := rows.Scan(&name, &parentName, &section, &kind, &synopsis, &goos, &goarch); err != nil {
+ if err := rows.Scan(&name, &parentName, &section, &kind, &synopsis); err != nil {
return fmt.Errorf("row.Scan(): %v", err)
}
- rowBC := internal.BuildContext{GOOS: goos, GOARCH: goarch}
- if bc.Match(rowBC) {
- resultsByBC[rowBC] = append(resultsByBC[rowBC], internal.SymbolMeta{
- Name: name,
- ParentName: parentName,
- Section: section,
- Kind: kind,
- Synopsis: synopsis,
- })
+ sm := internal.SymbolMeta{
+ Name: name,
+ ParentName: parentName,
+ Section: section,
+ Kind: kind,
+ Synopsis: synopsis,
}
- return nil
- }
-
- if err := db.db.RunQuery(ctx, q, collect, args...); err != nil {
- return nil, err
- }
-
- if len(resultsByBC) == 0 {
- return nil, derrors.NotFound
- }
-
- // Find the best build context among those that matched.
- var matchedBCs []internal.BuildContext
- for b := range resultsByBC {
- matchedBCs = append(matchedBCs, b)
- }
- sort.Slice(matchedBCs, func(i, j int) bool {
- return internal.CompareBuildContexts(matchedBCs[i], matchedBCs[j]) < 0
- })
- bestBC := matchedBCs[0]
-
- var symbols []*internal.Symbol
- symbolMap := make(map[string]*internal.Symbol)
- rows := resultsByBC[bestBC]
- for i := range rows {
- sm := rows[i]
if sm.ParentName != "" && sm.ParentName != sm.Name {
if parent, ok := symbolMap[sm.ParentName]; ok {
- parent.Children = append(parent.Children, &rows[i])
- continue
+ parent.Children = append(parent.Children, &sm)
+ return nil
}
}
// Treat as top-level if no parent or parent not found in this build context.
s := &internal.Symbol{
SymbolMeta: sm,
- GOOS: bestBC.GOOS,
- GOARCH: bestBC.GOARCH,
+ GOOS: uc.bestBC.GOOS,
+ GOARCH: uc.bestBC.GOARCH,
}
symbols = append(symbols, s)
symbolMap[sm.Name] = s
+ return nil
+ }
+
+ if err := db.db.RunQuery(ctx, q, collect, args...); err != nil {
+ return nil, err
+ }
+
+ if len(symbols) == 0 {
+ return nil, derrors.NotFound
}
return symbols, nil
}
@@ -144,15 +130,19 @@ func getPackageSymbols(ctx context.Context, ddb *database.DB, packagePath, modul
return sh, nil
}
-func packageSymbolQueryJoin(query squirrel.SelectBuilder, pkgPath, modulePath string) squirrel.SelectBuilder {
- return query.From("modules m").
- Join("units u on u.module_id = m.id").
- Join("documentation d ON d.unit_id = u.id").
- Join("documentation_symbols ds ON ds.documentation_id = d.id").
+func packageSymbolQuery(query squirrel.SelectBuilder) squirrel.SelectBuilder {
+ return query.From("documentation_symbols ds").
Join("package_symbols ps ON ps.id = ds.package_symbol_id").
- Join("paths p1 ON u.path_id = p1.id").
Join("symbol_names s1 ON ps.symbol_name_id = s1.id").
- Join("symbol_names s2 ON ps.parent_symbol_name_id = s2.id").
+ Join("symbol_names s2 ON ps.parent_symbol_name_id = s2.id")
+}
+
+func packageSymbolQueryJoin(query squirrel.SelectBuilder, pkgPath, modulePath string) squirrel.SelectBuilder {
+ return packageSymbolQuery(query).
+ Join("documentation d ON d.id = ds.documentation_id").
+ Join("units u on u.id = d.unit_id").
+ Join("modules m ON m.id = u.module_id").
+ Join("paths p1 ON u.path_id = p1.id").
Where(squirrel.Eq{"p1.path": pkgPath}).
Where(squirrel.Eq{"m.module_path": modulePath})
}
diff --git a/internal/postgres/unit.go b/internal/postgres/unit.go
index fee52439..3e13143b 100644
--- a/internal/postgres/unit.go
+++ b/internal/postgres/unit.go
@@ -418,17 +418,33 @@ func getPackagesInUnit(ctx context.Context, db *database.DB, fullPath, modulePat
return packages, nil
}
-func (db *DB) getUnitWithAllFields(ctx context.Context, um *internal.UnitMeta, fields internal.FieldSet, bc internal.BuildContext) (_ *internal.Unit, err error) {
- defer derrors.WrapStack(&err, "getUnitWithAllFields(ctx, %q, %q, %q)", um.Path, um.ModulePath, um.Version)
- defer stats.Elapsed(ctx, "getUnitWithAllFields")()
+// unitContext contains information for a unit that is used to fetch its
+// documentation or symbols.
+type unitContext struct {
+ unitID int
+ pathID int
+ moduleID int
+ isRedistributable bool
+ bcs []internal.BuildContext
+ bestBC internal.BuildContext
+ docID int
+ licenseMetas []*licenses.Metadata
+}
- // Get build contexts and unit ID.
- var pathID, unitID, moduleID int
- var bcs []internal.BuildContext
- var licenseMetas []*licenses.Metadata
- var isRedistributable bool
+// getUnitContext returns a unitContext for the given package path, module path,
+// version and build context. It finds all available build contexts for the
+// unit, selects the best matching one, and identifies the corresponding
+// documentation ID.
+func (db *DB) getUnitContext(ctx context.Context, pkgPath, modulePath, version string, bc internal.BuildContext) (_ *unitContext, err error) {
+ defer derrors.WrapStack(&err, "getUnitContext(ctx, %q, %q, %q, %v)", pkgPath, modulePath, version, bc)
+
+ var (
+ uc = &unitContext{}
+ // Map from build context to documentation ID.
+ docIDs = make(map[internal.BuildContext]int)
+ )
err = db.db.RunQuery(ctx, `
- SELECT d.goos, d.goarch, u.id, p.id, u.module_id, u.license_types, u.license_paths, u.redistributable
+ SELECT d.goos, d.goarch, u.id, p.id, u.module_id, u.license_types, u.license_paths, u.redistributable, d.id
FROM units u
INNER JOIN paths p ON p.id = u.path_id
INNER JOIN modules m ON m.id = u.module_id
@@ -438,43 +454,83 @@ func (db *DB) getUnitWithAllFields(ctx context.Context, um *internal.UnitMeta, f
AND m.module_path = $2
AND m.version = $3
`, func(rows *sql.Rows) error {
- var bc internal.BuildContext
- // GOOS and GOARCH will be NULL if there are no documentation rows for
- // the unit, but we still want the unit ID.
var (
- licenseTypes []string
- licensePaths []string
+ rowBC internal.BuildContext
+ docID sql.NullInt64
)
- if err := rows.Scan(database.NullIsEmpty(&bc.GOOS), database.NullIsEmpty(&bc.GOARCH), &unitID, &pathID, &moduleID, pq.Array(&licenseTypes), pq.Array(&licensePaths), &isRedistributable); err != nil {
+ // We need to scan all columns, but some are unit-level (constant across rows)
+ // and some are documentation-level (vary across rows).
+ var (
+ uIDCol, pIDCol, mIDCol int
+ licTypesCol, licPathsCol []string
+ isRedistCol bool
+ )
+
+ if err := rows.Scan(
+ database.NullIsEmpty(&rowBC.GOOS),
+ database.NullIsEmpty(&rowBC.GOARCH),
+ &uIDCol,
+ &pIDCol,
+ &mIDCol,
+ pq.Array(&licTypesCol),
+ pq.Array(&licPathsCol),
+ &isRedistCol,
+ &docID,
+ ); err != nil {
return err
}
- if db.bypassLicenseCheck {
- isRedistributable = true
+ // Assign unit-level metadata only once.
+ if uc.unitID == 0 {
+ uc.unitID = uIDCol
+ uc.pathID = pIDCol
+ uc.moduleID = mIDCol
+ uc.isRedistributable = isRedistCol
+ if db.bypassLicenseCheck {
+ uc.isRedistributable = true
+ }
+ lics, err := zipLicenseMetadata(licTypesCol, licPathsCol)
+ if err != nil {
+ return err
+ }
+ uc.licenseMetas = lics
}
- if bc.GOOS != "" && bc.GOARCH != "" {
- bcs = append(bcs, bc)
- }
- lics, err := zipLicenseMetadata(licenseTypes, licensePaths)
- if err != nil {
- return err
+ if rowBC.GOOS != "" && rowBC.GOARCH != "" {
+ uc.bcs = append(uc.bcs, rowBC)
+ if docID.Valid {
+ docIDs[rowBC] = int(docID.Int64)
+ }
}
- licenseMetas = lics
return nil
- }, um.Path, um.ModulePath, um.Version)
+ }, pkgPath, modulePath, version)
if err != nil {
return nil, err
}
- sort.Slice(bcs, func(i, j int) bool { return internal.CompareBuildContexts(bcs[i], bcs[j]) < 0 })
- var bcMatched internal.BuildContext
- for _, c := range bcs {
- if bc.Match(c) {
- bcMatched = c
- break
- }
+ if uc.unitID == 0 {
+ return nil, derrors.NotFound
+ }
+
+ uc.bcs = internal.SortedBuildContexts(uc.bcs)
+
+ var ok bool
+ uc.bestBC, ok = internal.MatchingBuildContext(uc.bcs, bc)
+ if ok {
+ uc.docID = docIDs[uc.bestBC]
+ }
+ return uc, nil
+}
+
+func (db *DB) getUnitWithAllFields(ctx context.Context, um *internal.UnitMeta, fields internal.FieldSet, bc internal.BuildContext) (_ *internal.Unit, err error) {
+ defer derrors.WrapStack(&err, "getUnitWithAllFields(ctx, %q, %q, %q)", um.Path, um.ModulePath, um.Version)
+ defer stats.Elapsed(ctx, "getUnitWithAllFields")()
+
+ uc, err := db.getUnitContext(ctx, um.Path, um.ModulePath, um.Version, bc)
+ if err != nil {
+ return nil, err
}
+
docSelect := "CAST(NULL AS bytea),"
if fields&internal.WithDocsSource != 0 {
docSelect = "d.source,"
@@ -503,27 +559,22 @@ func (db *DB) getUnitWithAllFields(ctx context.Context, um *internal.UnitMeta, f
LEFT JOIN readmes r
ON r.unit_id = u.id
- LEFT JOIN (
- SELECT synopsis, source, goos, goarch, unit_id
- FROM documentation d
- WHERE d.GOOS = $3 AND d.GOARCH = $4
- ) d
- ON d.unit_id = u.id
+ LEFT JOIN documentation d
+ ON d.id = $3
WHERE u.id = $2
`, docSelect)
var (
r internal.Readme
u internal.Unit
)
- u.BuildContexts = bcs
- var goos, goarch any
- if bcMatched.GOOS != "" {
- goos = bcMatched.GOOS
- goarch = bcMatched.GOARCH
- }
- doc := &internal.Documentation{GOOS: bcMatched.GOOS, GOARCH: bcMatched.GOARCH}
+ u.BuildContexts = uc.bcs
+ doc := &internal.Documentation{GOOS: uc.bestBC.GOOS, GOARCH: uc.bestBC.GOARCH}
end := stats.Elapsed(ctx, "getUnitWithAllFields-readme-and-imports")
- err = db.db.QueryRow(ctx, query, pathID, unitID, goos, goarch).Scan(
+ var docID any
+ if uc.docID != 0 {
+ docID = uc.docID
+ }
+ err = db.db.QueryRow(ctx, query, uc.pathID, uc.unitID, docID).Scan(
database.NullIsEmpty(&r.Filepath),
database.NullIsEmpty(&r.Contents),
database.NullIsEmpty(&doc.Synopsis),
@@ -546,17 +597,17 @@ func (db *DB) getUnitWithAllFields(ctx context.Context, um *internal.UnitMeta, f
}
end()
// Get other info.
- pkgs, err := db.getPackagesInUnit(ctx, um.Path, moduleID)
+ pkgs, err := db.getPackagesInUnit(ctx, um.Path, uc.moduleID)
if err != nil {
return nil, err
}
u.Subdirectories = pkgs
u.UnitMeta = *um
- u.Licenses = licenseMetas
- u.IsRedistributable = isRedistributable
+ u.Licenses = uc.licenseMetas
+ u.IsRedistributable = uc.isRedistributable
- if um.IsPackage() && !um.IsCommand() && bcMatched.GOOS != "" {
- u.SymbolHistory, err = GetSymbolHistoryForBuildContext(ctx, db.db, pathID, um.ModulePath, bcMatched)
+ if um.IsPackage() && !um.IsCommand() && uc.bestBC.GOOS != "" {
+ u.SymbolHistory, err = GetSymbolHistoryForBuildContext(ctx, db.db, uc.pathID, um.ModulePath, uc.bestBC)
if err != nil {
return nil, err
}
diff --git a/internal/testing/fakedatasource/fakedatasource.go b/internal/testing/fakedatasource/fakedatasource.go
index b44212a0..1075d147 100644
--- a/internal/testing/fakedatasource/fakedatasource.go
+++ b/internal/testing/fakedatasource/fakedatasource.go
@@ -140,13 +140,13 @@ func (ds *FakeDataSource) GetUnit(ctx context.Context, um *internal.UnitMeta, fi
// It can be a shallow copy, since we're only modifying the Unit.Documentation field.
u2 := *u
if fields&internal.WithDocsSource != 0 {
- if d := matchingDoc(u.Documentation, bc); d != nil {
+ if d := internal.DocumentationForBuildContext(u.Documentation, bc); d != nil {
u2.Documentation = []*internal.Documentation{d}
} else {
u2.Documentation = nil
}
} else if fields&internal.WithMain != 0 {
- if d := matchingDoc(u.Documentation, bc); d != nil {
+ if d := internal.DocumentationForBuildContext(u.Documentation, bc); d != nil {
u2.Documentation = []*internal.Documentation{{GOOS: d.GOOS, GOARCH: d.GOARCH, Synopsis: d.Synopsis}}
} else {
u2.Documentation = nil
@@ -157,23 +157,6 @@ func (ds *FakeDataSource) GetUnit(ctx context.Context, um *internal.UnitMeta, fi
return &u2, nil
}
-// matchingDoc returns the Documentation that matches the given build context
-// and comes earliest in build-context order. It returns nil if there is none.
-func matchingDoc(docs []*internal.Documentation, bc internal.BuildContext) *internal.Documentation {
- var (
- dMin *internal.Documentation
- bcMin *internal.BuildContext // sorts last
- )
- for _, d := range docs {
- dbc := d.BuildContext()
- if bc.Match(dbc) && (bcMin == nil || internal.CompareBuildContexts(dbc, *bcMin) < 0) {
- dMin = d
- bcMin = &dbc
- }
- }
- return dMin
-}
-
// GetUnitMeta returns information about a path.
func (ds *FakeDataSource) GetUnitMeta(ctx context.Context, path, requestedModulePath, requestedVersion string) (_ *internal.UnitMeta, err error) {
module := ds.findModule(path, requestedModulePath, requestedVersion)
@@ -350,19 +333,14 @@ func (ds *FakeDataSource) GetSymbols(ctx context.Context, pkgPath, modulePath, v
var bcs []internal.BuildContext
for b := range u.Symbols {
- if bc.Match(b) {
- bcs = append(bcs, b)
- }
+ bcs = append(bcs, b)
}
- if len(bcs) == 0 {
+ matchedBC, ok := internal.MatchingBuildContext(bcs, bc)
+ if !ok {
return nil, derrors.NotFound
}
- sort.Slice(bcs, func(i, j int) bool {
- return internal.CompareBuildContexts(bcs[i], bcs[j]) < 0
- })
-
- return u.Symbols[bcs[0]], nil
+ return u.Symbols[matchedBC], nil
}
// SearchSupport reports the search types supported by this datasource.