Skip to content

Commit

Permalink
feat: Add utility function for binary search based on symbol names (#273
Browse files Browse the repository at this point in the history
)

Also add clearer documentation related to guarantees around
document canonicalization.
  • Loading branch information
varungandhi-src committed Aug 2, 2024
1 parent 853034d commit 0504a34
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 3 deletions.
10 changes: 9 additions & 1 deletion bindings/go/scip/canonicalize.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package scip

// CanonicalizeDocument deterministically re-orders the fields of the given document.
// CanonicalizeDocument deterministically sorts and merges fields of the given document.
//
// Post-conditions:
// 1. The Occurrences field only contains those with well-formed ranges
// (length 3 or 4, potentially empty).
// 2. The Occurrences field is sorted in ascending order of ranges based on
// Range.CompareStrict
// 3. The Symbols field is sorted in ascending order based on the symbol name,
// and SymbolInformation values for the same name will have been merged.
func CanonicalizeDocument(document *Document) *Document {
document.Occurrences = CanonicalizeOccurrences(document.Occurrences)
document.Symbols = CanonicalizeSymbols(document.Symbols)
Expand Down
23 changes: 22 additions & 1 deletion bindings/go/scip/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package scip

import (
"sort"

"golang.org/x/exp/slices"
)

// FindSymbol returns the symbol with the given name in the given document. If there is no symbol by
// that name, this function returns nil.
// that name, this function returns nil. Prefer using FindSymbolBinarySearch over this function.
func FindSymbol(document *Document, symbolName string) *SymbolInformation {
for _, symbol := range document.Symbols {
if symbol.Symbol == symbolName {
Expand All @@ -16,6 +18,25 @@ func FindSymbol(document *Document, symbolName string) *SymbolInformation {
return nil
}

// FindSymbolBinarySearch attempts to find the SymbolInformation in the given document.
//
// Pre-condition: The symbols array must be sorted in ascending order based on the symbol name,
// and SymbolInformation values must be merged. This guarantee is upheld by CanonicalizeDocument.
func FindSymbolBinarySearch(canonicalizedDocument *Document, symbolName string) *SymbolInformation {
i, found := slices.BinarySearchFunc(canonicalizedDocument.Symbols, symbolName, func(sym *SymbolInformation, lookup string) int {
if sym.Symbol < lookup {
return -1
} else if sym.Symbol == lookup {
return 0
}
return 1
})
if found {
return canonicalizedDocument.Symbols[i]
}
return nil
}

// SortDocuments sorts the given documents slice (in-place) and returns it (for convenience). Documents
// are sorted in ascending order of their relative path.
func SortDocuments(documents []*Document) []*Document {
Expand Down
34 changes: 34 additions & 0 deletions bindings/go/scip/sort_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"
"golang.org/x/exp/slices"
"pgregory.net/rapid"
)

func TestFindOccurrences(t *testing.T) {
Expand Down Expand Up @@ -122,3 +125,34 @@ func TestSortRanges(t *testing.T) {
t.Errorf("unexpected occurrence order (-want +got):\n%s", diff)
}
}

func genSymbolInfo() *rapid.Generator[*SymbolInformation] {
return rapid.Custom(func(t *rapid.T) *SymbolInformation {
return &SymbolInformation{Symbol: rapid.String().Draw(t, "symbol")}
})
}

func TestFindSymbolBinarySearch(t *testing.T) {
rapid.Check(t, func(t *rapid.T) {
symbolInfoGen := genSymbolInfo()
symbolInfos := rapid.SliceOfN(symbolInfoGen, 0, 10).Draw(t, "symbolInfos")
doc := &Document{Symbols: symbolInfos}
canonicalDoc := CanonicalizeDocument(doc)
for _, symbolInfo := range symbolInfos {
got := FindSymbolBinarySearch(canonicalDoc, symbolInfo.Symbol)
require.NotNil(t, got)
require.Equal(t, symbolInfo.Symbol, got.Symbol)
}
other := rapid.String().Draw(t, "otherSymbol")
isInOriginalSlice := slices.ContainsFunc(symbolInfos, func(info *SymbolInformation) bool {
return info.Symbol == other
})
got := FindSymbolBinarySearch(canonicalDoc, other)
if isInOriginalSlice {
require.NotNil(t, got)
require.Equal(t, other, got.Symbol)
} else {
require.Nil(t, got)
}
})
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/sourcegraph/sourcegraph/lib v0.0.0-20220511160847-5a43d3ea24eb
github.com/stretchr/testify v1.8.4
github.com/urfave/cli/v2 v2.25.7
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1
golang.org/x/tools v0.12.0
google.golang.org/protobuf v1.31.0
pgregory.net/rapid v1.1.0
Expand Down
4 changes: 3 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
Expand Down Expand Up @@ -661,4 +663,4 @@ mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ=
mvdan.cc/gofumpt v0.5.0 h1:0EQ+Z56k8tXjj/6TQD25BFNKQXpCvT0rnansIc7Ug5E=
mvdan.cc/gofumpt v0.5.0/go.mod h1:HBeVDtMKRZpXyxFciAirzdKklDlGu8aAy1wEbH5Y9js=
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=

0 comments on commit 0504a34

Please sign in to comment.